2012-03-23 9 views
9

Sto usando Backbone e quindi Underscore per il rendering dei miei modelli. I miei modelli vengono renderizzati nei tag <script> e quindi uso jQuery per prendere il loro html. La mia opinione spina dorsale si presenta così:Come memorizzare correttamente un modello javascript, in modo che non venga istanziato più volte

App.ItemView = Backbone.View.extend({ 
    className:'well', 

    events: { 
     'click .continue': 'handleContinueClick', 
    }, 

    initialize: function() { 
     this.template = _.template($("#ItemTemplate").html()) 
     this.render() 
    }, 

    render: function() { 
     $(this.el).html(this.template({model:this.model})) 
    }, 

    handleContinueClick: function(e) { 
     alert('Clicked!') 
    } 
}) 

mia edizione è vorrei solo andare e afferrare l'html una volta e solo una volta per questo particolare tipo di vista in modo che se ho un sacco di oggetti non è così vai alla ricerca del codice html per questo modello ogni volta.

In pratica, come si memorizza correttamente la variabile di modello al livello dell'oggetto ItemView (non l'istanza della vista) tenendo presente che il recupero dell'HTML deve attendere fino al termine del caricamento della pagina (in modo da garantire il modello html è disponibile).

+0

c'è un motivo ti stai preoccupando di questo? Spero che tu capisca i mali dell'ottimizzazione prematura. – JayC

+0

certo, se qualcuno può dirmi che non importa, io sono figo con quello. hai ragione è prolly presto, sono solo curioso – MattoTodd

+1

@JayC - stai praticamente dicendo "L'accesso DOM per la stessa cosa, più volte, è l'ottimizzazione prematura", che è piuttosto lontano dalla verità. Se i dati non cambiano mai, richiedili solo una volta. –

risposta

16

È possibile costruire un oggetto molto semplice che memorizza nella cache i modelli per voi:


TemplateCache = { 
    get: function(selector){ 
    if (!this.templates){ this.templates = {}; } 

    var template = this.templates[selector]; 
    if (!template){ 
     var tmpl = $(selector).html(); 
     template = _.template(tmpl); 
     this.templates[selector] = template; 
    } 

    return template; 
    } 
} 

Poi a suo avviso, è possibile chiamare TemplateCache.get e passare il vostro selettore modello.


Backbone.View.extend({ 
    template: "#ItemTemplate", 

    render: function(){ 
    var template = TemplateCache.get(this.template); 
    var html = template(this.model.toJSON()); 
    this.$el.html(html); 
    } 
}); 

La prima volta che si chiama TemplateCache.get per un dato selettore, caricherà dal DOM. Qualsiasi chiamata successiva per ottenere il modello lo caricherà dalla versione memorizzata nella cache e impedirà la chiamata di accesso DOM aggiuntiva.

FWIW: Ho una versione molto più robusto dell'oggetto TemplateCache nel mio quadro Backbone.Marionette: https://github.com/derickbailey/backbone.marionette

+0

Esiste un vantaggio misurabile nel fare le cose in questo modo? No davvero, sono curioso. E perché non dovresti andare avanti e usare _.template nel tuo TemplateCache? – JayC

+2

Giusto per chiarire: perché non dovresti memorizzare nella cache il costruttore del modello anziché la stessa stringa html? – JayC

+0

bella implementazione pulita e buona separazione delle preoccupazioni! – Chris

1

Si potrebbe impazzire con prototype.template a mano e compilare il modello la prima volta che si crea un'istanza della vostra vista. Qualcosa di simile a questo:

initialize: function() { 
    if(!this.constructor.prototype.template) 
     this.constructor.prototype.template = _.template($("#ItemTemplate").html()); 
    this.render(); 
} 

Demo: http://jsfiddle.net/ambiguous/e6y3F/

Il trucco è quello di mettere le mani sulla destra prototype.

+0

il metodo renderizza il modello e lo analizza. Se il modello richiede dati di input per il rendering, è necessario lasciare template.render() per la funzione di modello della vista. – supershnee

+0

@supershnee: Non sei sicuro di cosa intendi, '_.template (html)' restituisce semplicemente la funzione di modello di compilazione, non vedo alcuna chiamata a 'this.template()' dovunque e 'template.render()' potrebbe solo produrre un TypeError come compilato I template Underscore non hanno metodi 'render'. La tua risposta qui sotto è praticamente la stessa cosa della mia. –

+0

Siamo spiacenti, significa this.render(). – supershnee

5

Molti esempi di Backbone che ho visto lo fanno in questo modo. Questo attraverserà il DOM solo una volta per analizzare il modello quando la pagina finisce di caricarlo e utilizzarlo per ogni new ItemView().

App.ItemView = Backbone.View.extend({ 
    template: _.template($("#ItemTemplate").html()), 

    className:'well', 

    events: { 
     'click .continue': 'handleContinueClick', 
    }, 

    ... 
}); 

http://backbonejs.org/docs/todos.html#section-21

+3

C'è un pericolo nel farlo, in quanto il tuo modello potrebbe non caricarsi affatto. http://lostechies.com/derickbailey/2011/11/09/backbone-js-object-literals-views-events-jquery-and-el/ –

+2

Se questo codice appare dopo il modello (e quindi dopo che il modello è stato caricato), quindi non dovrebbe esserci alcun pericolo. Semplice e leggibile. Questa è la mia tecnica preferita. –

1

è possibile memorizzare il modello compilato in una chiusura in modo che solo le istanze di ItemView possano accedervi:

(function() { 

    var template; 

    App.ItemView = Backbone.View.extend({ 

     className:'well', 

     events: { 
      'click .continue': 'handleContinueClick' 
     }, 

     initialize: function() { 
      this.render(); 
     }, 

     render: function() { 
      template = template || _.template($("#ItemTemplate").html()); 
      $(this.el).html(template({model:this.model})); 
     }, 

     handleContinueClick: function(e) { 
      alert('Clicked!'); 
     } 

    }); 

})(); 
1

Un'altra soluzione utilizzando prototipo:

initialize: function(option) { 
    if (!this.template) App.ItemView.prototype.template = this.template || _.template($('ItemTemplate').html()); 
} 
0

Potresti usare codice HTML grezzo o potresti ottenere codice HTML da elemento DOM che s hould aveva reso prima di questo script

1) grezzo codice HTML:

var app = app || {}; 

app.TreeView = Backbone.View.extend({ 
    tagName: 'ul', 
    id: 'js-tree', 
    template: _.template('<li data-id="<%- id %>"><%- Name %></li>'), 
    initialize: function() { 
     this.render(); 
    }, 
    render: function() { 
     this.model.each(this.renderRow, this); 
     return this; 
    }, 
    renderRow: function(model) { 
     var html = template(model); 
     this.$el.append(html); 
     return this; 
    } 
}); 

2) o HTML-codice reso DOM-elemento:

var app = app || {}; 

app.TreeView = Backbone.View.extend({ 
    tagName: 'ul', 
    id: 'js-tree', 
    template: _.template($("#js-template-tree-item").html()), 
    initialize: function() { 
     this.render(); 
    }, 
    render: function() { 
     this.model.each(this.renderRow, this); 
     return this; 
    }, 
    renderRow: function(model) { 
     var html = template(model); 
     this.$el.append(html); 
     return this; 
    } 
}); 
Problemi correlati