2012-02-09 16 views
6

Sto provando a creare una piccola app backbone.js, ma ho problemi con l'ordine delle cose.backbone.js visualizza i rendering prima del recupero del modello

Nel mio file html, ho due blocchi di script nell'intestazione:

<script type="text/template" id="model-template"> 
    <a href="#"><%= title %></a> 
</script> 

<script type="text/javascript"> 
    jQuery(function(){ 
    window.model.fetch(); 
    }); 
</script> 

Nei miei app.js, ho definito un modello semplice, vista e un router.

(function($) { 

window.MyModel = Backbone.Model.extend({ 
    url: '/mymodel' 
}); 

window.mymodel = new MyModel(); 

$(document).ready(function() { 

    window.MyModelView = Backbone.View.extend({ 
     template: _.template($('#mymodel-template').html()), 

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

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

window.MyApp = Backbone.Router.extend({ 
    routes: { 
     '': 'home' 
    }, 

    initialize: function() { 
     this.myModelView = new MyModelView({ 
      model: window.mymodel 
     }); 
    }, 

    home: function() { 
     var $body = $('body'); 
     $body.empty(); 
     $body.append(this.myModelView.render().el); 
    } 
}); 

$(function() { 
    window.App = new MyApp(); 
    Backbone.history.start({pushState: true}); 
}); 

})(jQuery); 

L'applicazione è servita da una semplice applicazione sinatra. L'URL /mymodel serve un file JSON statica:

{ 
    "title": "My Model", 
} 

Quando si carica l'applicazione, ottengo un errore nella console javascript:

Uncaught ReferenceError: title is not defined 

Il problema sembra essere, che la vista si rende prima il modello viene prelevato dal server. Perché?

Ieri, ho seguito i primi due screencast di backbone.js da PeepCode. Ho provato a confrontare la mia applicazione con quella che è uscita dagli screencast, ma non vedo un motivo per cui la mia applicazione voglia funzionare.

Qualche suggerimento?

+0

Mi ci sono volute ore ... ORE per capire che questo è il motivo per cui la mia app non funzionava, funzionava alcune volte non altre, o funzionava su alcuni browser e non su altri. Tipo di errore da principiante quando pensi a come dovrebbe funzionare, suppongo. Lezione appresa. Penso che troppi esempi di Backbone utilizzino dati hard coded e concetti molto importanti come questo al margine della strada. – IcedDante

risposta

16

In questo caso, è necessario eseguire il bootstrap dei dati del modello in modo che sia disponibile al caricamento della pagina.

Invece di

window.model.fetch(); 

messo qualcosa di simile (se si utilizza un .erb)

<script> 
    window.model = new MyModel(<%= @model.to_json %>); 
</script> 

In caso contrario è necessario per rendere la vista una volta che il modello è inverosimile per esempio

legano la vista per rendere quando il modello cambia

initialize: function() { 
    _.bindAll(this, 'render'); 

    this.model.on("change", this.render); 
}, 

o gestire il successo del modello.recuperare e rendere la vista

window.model.fetch({ 
    success: function (model, response) { 
     window.MyApp.myModelView.render(); 
    } 
}); 
+0

Questa risposta mi ha avvicinato alla soluzione. Ho rimosso la chiamata per eseguire il rendering nel mio router, ma ho mantenuto il codice che ha aggiunto la proprietà'el 'delle viste al documento. Quindi, quando il recupero è completato, cambia il mio modello, che genera l'evento change, che chiama il metodo render. – Vegar

1

Ripristina lo stato del modello dal server. Utile se il modello ha non è mai stato popolato con i dati, o se desideri assicurarti che lo stato del server più recente sia . Un evento "change" verrà attivato se lo stato del server differisce dagli attributi correnti. Accetta callback di errore e di errore nell'hash delle opzioni, che vengono passati (modello, risposta) come argomenti.

In questo caso, si desidera eseguire il rendering della vista nella richiamata di successo.

model.fetch({ 
    error: function() { 
    }, 
    success: function (model, response) { // model is ready now 
     // do view stuff here 
    } 
}); 
1

chiaramente dalle risposte precedenti, si sa che è necessario per rendere il callback prendere successo, ma penso che problema è un po 'più di questo. In particolare, la tua rotta di casa viene utilizzata per creare immediatamente myModelView piuttosto che quando i suoi dati vengono caricati. Questo sta accadendo perché stai chiamando render() per accodare il corpo. Invece, prova e inizializza la vista con un elemento esistente. In questo modo è possibile ritardare la richiesta di rendere fino a prendere ha completato:

window.MyApp = Backbone.Router.extend({ 
    routes: { 
     '': 'home' 
    }, 

    initialize: function() { 

    }, 

    home: function() { 
     var $body = $(document.body).empty(); 
     var myModelEl = $("<div></div>").appendTo($body); 

     this.myModelView = new MyModelView({ 
      model: window.mymodel, 
      el: myModelEl 
     }); 
    } 
}); 

Ora, non hai chiamato render() ancora, ma il tuo punto di vista è collegata con successo al DOM come desiderato.

+0

Quindi, stai creando un'istanza di MyModelView sia nella funzione initialize che home? O dovrei rimuoverlo dall'inizializzazione? – Vegar

+0

Sei corretto, rimuovilo dall'inizializzazione. Dispiace per la confusione. Non ha sempre senso inserirli nell'inizializzazione in ogni caso, poiché tipicamente si renderanno viste diverse a seconda del metodo di instradamento chiamato. –

5

È inoltre possibile usufruire dell'oggetto differita http://api.jquery.com/category/deferred-object/ che Backbone.Model.fetch() rendimenti, in questo modo:

window.MyModel = Backbone.Model.extend({ 
    url: '/mymodel' 
}); 

//when the model is fetched, store a reference to the jQuery deferred object 
window.MyModelFetch = window.MyModel.fetch(); 

window.MyModelView = Backbone.View.extend({ 
    template: _.template($('#mymodel-template').html()), 

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

    render: function() { 
     //reference the deferred object and use 'done' method 
     //to register a callback after complete 
     window.MyModelFetch.done(function(){ 
      var renderedContent = this.template(this.model.toJSON()); 
      $(this.el).html(renderedContent); 
      return this; 
     } 
    } 
}); 

si consiglia di creare un'estensione del modello di spina dorsale che memorizza un oggetto differita sul modello che si può di riferimento, in questo modo:

Backbone.DeferrableModel = Backbone.Model.extend({ 
     fetch: function(){ 
      this.fetching = Backbone.Model.prototype.fetch.apply(this, arguments); 
      return this.fetching; 
     } 
}); 

Quindi secondo lei metodo render, si può solo dire questo:

render: function() { 
     //the deferred reference is now directly referenced from your model 
     this.model.fetching.done(function(){ 
      var renderedContent = this.template(this.model.toJSON()); 
      $(this.el).html(renderedContent); 
      return this; 
     } 
    }  

Può essere molto utile utilizzare un modello esteso e seguire questo schema in tutta l'applicazione backbone.

Problemi correlati