Backbone.js (Sub)View renderovací trik

V Backbone.js je vykreslování pohledů opravdu jednoduché, ale ne tolik. Je to jednoduché, protože vás Backbone nenutí, abyste to dělali nějakým konkrétním způsobem, takže máte svobodu použít jen trochu jQuery a vložit HTML do prvku. Pak znovu, protože samo o sobě nic neimplementuje, uvízli jsme v psaní vlastních implementací, což je obtížnější, než by jinak mohlo být. V případě vykreslování dílčích náhledů to může být určitě trochu složitější.

Vím, že jsem řekl, že na nějakou dobu odložím další věci Backbone.js, ale musel jsem to udělat. Nejprve jsem četl o tomto triku na blogu Iana Storma Taylora a rozhodl jsem se, že stojí za to ho šířit. Za druhé, nejsem připravený psát o tématu, které jsem plánoval jako další, takže to bude muset chvíli počkat.

Požadavky

V původním příspěvku Iana Taylora o triku otevřel seznam některých požadavků, které by měly být splněny pro implementaci vykreslování podview. Nejprve však musíte plně porozumět tomu, co se děje, když mluvíme o dílčích zobrazeních.

Pokud se podíváte zpět na aplikaci Wine Cellar, kterou jsme vytvořili v mé sérii screencastů Backbone.js, uvidíte, že postranní panel je vytvořen z jediného zobrazení, které vytváří další zobrazení pro každou položku seznamu. To není to, o čem mluvíme. Pokud vezmete celou část těla z této aplikace, vytvoříte pohled, který spravuje všechny tři oblasti zobrazení (záhlaví, postranní panel a hlavní), pak by tyto tři oblasti byly typem dílčích pohledů, o kterých mluvíme. Hlavní pohled by obsahoval odkaz na každý ze tří pohledů a vykresloval by je na správných místech. Spíše než pomocí Routeru k nastavení zobrazení, jako jsme to dělali v aplikaci Vinný sklep, bychom k nastavení ostatních zobrazení použili super zobrazení.

Nyní, když jsme na stejné stránce, pojďme se podívat na požadavky, které měl Ian:

  1. render by mělo být možné volat vícekrát bez vedlejších účinků. „Současný způsob“ dělání věcí často naruší posluchače událostí na dílčích zobrazeních. Cokoli, co se takto rozbije, je vedlejší efekt.
  2. _ Pořadí modelu DOM by mělo být deklarováno v šablonách, nikoli v JavaScriptu._ Takže místo toho, abychom definovali pořadí dílčích zobrazení v rámci funkce vykreslení, pouze přiřadíme dílčí zobrazení do různých oblastí ve struktuře DOM šablony.
  3. Volání render opět by měl zachovat stav, ve kterém byl pohled. Pokud se stav nezměnil, pak by volání vykreslení v jakémkoli zobrazení (super nebo dílčí) nemělo způsobit žádné změny toho, co je již vykresleno.
  4. _Dvojité vykreslení by nemělo zahodit zobrazení, jen aby je bylo možné znovu rekonstruovat. _Tohle je docela samovysvětlující. Nepředělávejte dokonale dokonalý subview. Pokud je ve stavu, ve kterém chcete, aby byl, nechte to být.

Implementace

Nejprve se podívejme, jak by to někdo mohl normálně udělat:

1
2
3
4
5
6
render: function() {
this.$el.html(this.template(options));
this.$('.subview').html(this.subview.render());
this.$('.othersubview').html(this.othersubview.render());
return this.el;
}

Všimněte si, že tento kód předpokládá, že render metoda dílčích zobrazení vždy vrací zobrazení el , stejně jako tento vnější render funkce ano. Dávám přednost tomu, aby to udělal můj kód. Viděl jsem, že spousta lidí vrací this . To dává smysl, pokud chcete věci zřetězit, ale v 95 % případů nakonec napíšete toto:

1
view.render().el

To je ošklivé (podle mého názoru), protože odkazujete na vlastnost za funkcí. Pokud již máte odkaz na zobrazení a pokud v 95 % případů budete pouze žádat o el tak jako tak, proč to trochu nezjednodušíme a nevrátíme this.el z render funkce?

Každopádně zpět k prvnímu úryvku kódu. Možná si to neuvědomujete, ale tohle má vážnou chybu. Když zavoláte html jQuery jQuery nejprve zavolá empty na aktuálním obsahu, odstranění všech vázaných obslužných rutin událostí na prvcích. Když zavoláte na render ve vašich dílčích zobrazeních tyto události nebudou znovu svázány, takže zůstanete u statického HTML a bez posluchačů událostí.

Jedním ze způsobů, jak to vyřešit, je zavolat delegateEvents() v rámci každých render funkce dílčích zobrazení, ale to je jen další krok, který musíte zahrnout do každého dílčího zobrazení. Někteří lidé místo toho pouze znovu vytvoří dílčí zobrazení, což způsobuje příliš mnoho režie a zbytečné výpočty.

Lepší způsob

Pan Taylor poznamenává, že pomocí setElement na subviews funguje opravdu dobře. Když zavoláte na setElement , předaný argument se stane novým prvkem pro dílčí zobrazení (nahrazuje this.el v podnáhledu). Způsobuje také delegateEvents být znovu volán v dílčím pohledu, takže posluchači událostí budou znovu přiřazeni. Takže naše render funkce by nyní vypadala takto:

1
2
3
4
5
6
render: function() {
this.$el.html(this.template(options));
this.subview.setElement(this.$('.subview')).render();
this.othersubview.setElement(this.$('.othersubview')).render();
return this.el;
}

To je však trochu otravné spravovat, a tak vytvořil funkci, kterou přidal do vnějšího pohledu, který nazývá assign , který by vypadal takto:

1
2
3
assign: function(view, selector) {
view.setElement(this.$(selector)).render();
}

Pak už jen použije assign v rámci jeho render funkce:

1
2
3
4
5
6
render: function() {
this.$el.html(this.template(options));
this.assign(this.subview, '.subview');
this.assign(this.othersubview, '.othersubview');
return this.el;
}

To se pak stará o všechny požadavky, které má, ale nebyl s tím spokojen. Později napsal druhý příspěvek na toto téma, kde uvádí, že se podíval na Layout Manager a viděl, že používá stejný koncept. Ale ukázalo to Ianovi způsob, jak zlepšit jeho assign funkce trochu, což by změnilo poslední úryvek kódu, který jsme napsali, na toto:

1
2
3
4
5
6
7
8
9
10
render: function() {
this.$el.html(this.template(options));

this.assign({
'.subview': this.subview,
'.othersubview': this.othersubview
});

return this.el;
}

Jedná se o drobné vylepšení, díky kterému se méně opakuje, protože vyžaduje pouze volání assign jednou. Zde je nový assign metoda vypadá takto:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
assign: function (selector, view) {
var selectors;

if (_.isObject(selector)) {
selectors = selector;
}
else {
selectors = {};
selectors[selector] = view;
}

if (!selectors) return;

_.each(selectors, function (view, selector) {
view.setElement(this.$(selector)).render();
}, this);
}

Závěr

Díky moc Ian Storm Taylor za vaše postřehy! Určitě to budu používat ve svých menších aplikacích, ale až se dostanu do trochu větších aplikací, myslím, že se hlouběji podívám na Backbone.LayoutManager. Máte nějaké opravdu skvělé „triky“, které používáte ve svých aplikacích Backbone.js? Podělte se o ně v komentářích níže! Bůh žehnej a šťastné kódování!