2012-02-09 28 views
6

Próbuję utworzyć małą aplikację backbone.js, ale zmagam się z kolejnością rzeczy.renderowanie widoku backbone.js przed pobieraniem modelu

W moim pliku html, mam dwa bloki skrypt w nagłówku:

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

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

W moich app.js, mam zdefiniowane prosty model, widok i routera.

(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); 

Aplikacja jest obsługiwana przez prostą aplikację Sinatra. Url /mymodel służy statyczny plik json:

{ 
    "title": "My Model", 
} 

Podczas ładowania aplikacji, pojawia się błąd w konsoli javascript:

Uncaught ReferenceError: title is not defined 

Problem wydaje się, że widok renderuje się przed model jest pobierany z serwera. Dlaczego?

Wczoraj poszedłem do pierwszych dwóch screencastów backbone.js z PeepCode. Próbowałem porównać moją aplikację z tą, która pojawiła się w screencastach, ale nie widzę powodu, dla którego moja aplikacja wymaga pracy.

Wszelkie sugestie?

+0

Zajęło mi godziny ... GODZIN, aby dowiedzieć się, że to dlatego moja aplikacja nie działała, działała kilka razy, nie pracowała nad niektórymi przeglądarkami, a nie nad innymi. Rodzaj pomyłki debiutanta, kiedy myślisz o tym, jak powinno działać moje pobieranie. Lekcja. Myślę, że zbyt wiele przykładów szkieletów wykorzystuje surowo zakodowane dane i bardzo ważne koncepcje, takie jak ten upadek. – IcedDante

Odpowiedz

16

W takim przypadku należy załadować dane modelu, aby był on dostępny podczas ładowania strony.

Zamiast

window.model.fetch(); 

umieścić coś takiego w (w przypadku korzystania z .erb)

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

W przeciwnym razie trzeba renderować widok gdy model jest naciągane na przykład

wiążą widok do renderowania kiedy model zmienia

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

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

lub uchwyt sukces modelu.pobierania i renderowania widoku

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

Ta odpowiedź przybliżyła mnie do rozwiązania. Usunąłem wywołanie renderowania w moim routerze, ale zachowałem kod, który dodał właściwość widoku'el 'do dokumentu. Tak więc po zakończeniu pobierania zmienia mój model, który zgłasza zdarzenie change, które wywołuje metodę renderowania. – Vegar

1

Resetuje stan modelu z serwera. Przydatne, jeśli model nigdy nie został wypełniony danymi, lub jeśli chcesz mieć najnowszy stan serwera, który ma być . Zdarzenie "zmiana" zostanie wywołane, jeśli stan serwera różni się od bieżących atrybutów. Przyjmuje wywołania zwrotne dotyczące powodzenia i błędu w mieszaniu opcji, które są przekazywane jako argumenty w postaci (model, odpowiedź).

W takim przypadku należy renderować widok w oddzwanianiu powodzenia.

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

wyraźnie z poprzednich odpowiedzi, wiesz, że trzeba uczynić na zwrotnego przynieść sukces, jednak myślę, że problemem jest to nieco więcej niż to. Mianowicie, twoja główna trasa służy do natychmiastowego budowania myModelView, a nie wtedy, gdy ładuje się dane. Dzieje się tak, ponieważ wywołujesz metodę render() w celu dołączenia do treści. Zamiast tego spróbuj zainicjować widok z istniejącym elementem. W ten sposób można opóźnić wezwanie do renderowania aż sprowadzić zakończył:

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 
     }); 
    } 
}); 

Teraz nie nazwali render() jeszcze, ale twój widok jest z powodzeniem dołączony do DOM zamierzonych.

+0

A więc tworzysz instancję MyModelView zarówno w funkcji initialize, jak i home? Czy powinienem usunąć go z inicjowania? – Vegar

+0

Masz rację, usuń go z inicjowania. Przepraszam za zamieszanie. Nie zawsze ma sens wstawienie go do inicjalizacji, ponieważ zazwyczaj renderujesz różne widoki w zależności od metody wywoływanej trasy. –

5

Można również skorzystać z odroczonym obiektu http://api.jquery.com/category/deferred-object/ że Backbone.Model.fetch() powraca, tak:

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; 
     } 
    } 
}); 

Możesz utworzyć rozszerzenie modelu szkieletu, który przechowuje odroczonego obiekt modelu, który można odniesienia, podobnie jak to:

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

Następnie w widoku czyni metodę, można po prostu powiedzieć:

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; 
     } 
    }  

Korzystanie z rozszerzonego modelu może być bardzo wygodne i stosować się do tego wzoru w całej aplikacji szkieletowej.

Powiązane problemy