Screenshot Posledně jsem se rozepsal nad mez odolnosti roztěkaného novodobého čtenáře, tak jsem se raději utnul a tři ozvěny Polymer Hackathonu jsem si ještě pošetřil. Jdeme na to.

core-icon

Na zahřátí si dáme něco lehčího - core-icon. To je pěkná ukázka toho, jak má člověk dělat Polymer elementy. Krásně znovupoužitelné atomické tagy, které můžete plácnout kdykoliv a kdekoliv. Konkrétně core-icon do mrtě rozebral Rob Dodson na Google Developers Youtube kanálu:

Já jsem ji použil na vykreslení indikátoru velikosti planety:

<tr template repeat="{{ planet in planets | enumerate }}" ... >
    <td>{{planet.index + 1}}</td>
    <th class="left">{{planet.value.name}}</th>
    <td class="right">{{planet.value.distance}} parsecs</td>
    <td><core-icon icon="check-circle-blank"
        style="width: {{planet.value.size * 10}}px; height: {{planet.value.size * 10}}px"></core-icon></td>
    ...
</tr>

Povšiměte si především kreativní práce s width a height. Polopatické, že? V reálném nasazení bych to možná nedělal takhle, minimálně bych tam nedával px ale alespoň em. Ikona krasně scaluje, protože SVG.

Banalita, ale potěší.

MMCH - příklad taky ukazuje, jak iterovat přes seznam objektů s ukládáním pořadového čísla - “| enumerate”. Kdybych iteroval bez “| enumerate”, budu místo planet.value.distance psát planet.distance, ale na planet.index bych mohl zapomenout.

Observables

Jak vidno, vypisuji v příkladu seznam planet. Seznam je utřízený podle vzdálenosti. Jakmile hráč “zawarpuje” jinam, seznam se překreslí. Abych mohl v seznamu použít “{{planets}}” a přitom se mohl spolehnout, že se seznam přerenderuje vždy když ho přetřídím, musím z něj udělat “Observable”.

sortedPlanets = toObservable(game.planets);

Prosté:

@observable List<Planet> sortedPlanets;

… nám zajistí sledování toho, že se změnila hodnota vlastnosti sortedPlanets (změna na jiný list), ale už nebude sledovat změny, které se dějí s prvky listu.

Pokud chci sledovat změny, které se dějí v doménovém modelu (například class Player) musím si model mírně zasvinit podporou Observables.

class Player extends Observable {

  @observable int money = 5000;
  @observable int fuel = 1000;

  ...
}

Když pak v šabloně použiju {{game.player.money}}, můžu si být jistý, že kdykoliv změním money, přerenderuje se i šablona.

No jo, ale jak je to vlastně uděláno? Jaký démon hlubin pekelných sleduje změny té proměnné? Přece Polymer neustále nescanuje stav proměnných které vykreslil? Pochopitelně že ne. Kdybych to napsal nějak takhle:

get money => _money;
set money(val) {
    _money = notifyPropertyChange(#money, _money, val);
}

Tak už by to taková záhada nebyla, že jo. No a to se právě děje. Bežte do ‘dart:observe’ a podívejte se na ‘transformer.dart’. Během buildu se pomocí transformeru vaše magické @observable přepíše na get, set a notifyPropertyChange.

Prosim Vás. Nemyslete si, že jsem šel studovat zdrojáky jaderných knihoven Dartu. Přišel jsem na to tak, že jsem napsal:

@observable Neco neco = nejakaChyba;

Transformer to přepsal a kompilátor pak nahlásil, že to takhle nejde - a při té příležitosti napráskal, jak to ten transformer vlastně přepsal.

Zákeřné optimalizace změn DOMu

Na závěr jsem si nechal špek největší. Hele nahoru, tam jak iteruju přes planety a vypisuju si je do tabulky.

planet.value.distance je vzdálenost planety od hráče. Player je součástí Game, Planet taky, Planet zná Game tudíš Planet zná i hráče a může říct jak je od něj daleko. Je asi sporné, jestli má Planet tuto metodu poskytovat, jestli by to neměl dělat nějaký nástroj, který bude nad modelem operovat. Ale - udělal jsem to takhle.

No jo, ale distance není žádná observable property, která by notifikovala svoje změny. Je to složitější výpočet. Z pohledu Polymer template se tedy nemění. To se projevovalo mysteriózní bugou. Pokud hráč odwarpoval jinam, planety jsem zesortoval a výpis planety se automaticky překreslil. Protože:

sortedPlanets = toObservable(game.planets);

Ale pokud se pořadí planety v listu nezměnilo, její konkrétní řádek se vůbec nepřerenderoval, zůstal tak jak byl - sice se změnila distance, ale ne z pohledu Polymeru. To podle mě na straně templatů obnáší netriviální/obdivuhodné optimalizace změn DOMu … ale jak z toho ven?

Jednou cestou je ComputedProperty. S tím ale praktickou zkušenost nemám. Během hackathonu jsem se vzmohl jen na dirty hack - v Polymer template jsem použil množství paliva Playera, tím jsem zaregistroval template k poslouchání změn jeho stavu. Když se změní palivo, template řádku planety se překreslí. A tedy nakreslí i správnou novou vzdálenost - změna paliva = změna polohy.

<td ... data-dirty-hack-fuel="{{player.fuel}}" .../>

Tak takhle to nedělejte, m’key? Je to velmi poučné, ale naprosto špinavé :-)

No a to je všechno. Příště se donutím ke sdílení čerstvých zkušeností z tvorby we.are.hiring.cz. Pokud tedy nebudu plný dojmů ze zítřejšího DevFest.