Backbone.js view render before fetching a model

I am trying to make a small backbone.js application, but am struggling with the order of things.

In my html file, I have two script blocks in the header:

<script type="text/template" id="model-template"> <a href="#"><%= title %></a> </script> <script type="text/javascript"> jQuery(function(){ window.model.fetch(); }); </script> 

In my app.js app, I defined a simple model, view, and 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); 

The application is served by a simple sinatra application. The URL /mymodel is for a static json file:

 { "title": "My Model", } 

When loading the application, I get an error message in the javascript console:

 Uncaught ReferenceError: title is not defined 

The problem is that the view is displayed before the model is retrieved from the server. Why is this?

Yesterday I ran the first two backbone.js scripts from PeepCode. I tried to compare my application with the one that came out of the screencasts, but cannot explain the reason why my application wants to work.

Any suggestions?

+6
source share
5 answers

In this case, you must download the model data so that it is available when the page loads.

Instead

 window.model.fetch(); 

put something like this in (if using .erb)

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

Otherwise, you need to display the view as soon as the model is retrieved, for example.

bind the view to rendering when the model changes

 initialize: function () { _.bindAll(this, 'render'); this.model.on("change", this.render); }, 

or handle the success of model.fetch and display the view

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

You can also use the deferred object http://api.jquery.com/category/deferred-object/ , which returns Backbone.Model.fetch() , for example:

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

You might want to create a base model extension that stores a pending object on your model that you can reference, for example:

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

Then in your visualization method, you can simply say the following:

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

It is very convenient to use the advanced model and follow this template throughout your base application.

+5
source

Resets the model state from the server. Useful if the model has never been populated with data or if you want you to have the latest server state. The change event will be fired if the server state is different from the current attributes. Accepts successful and error callbacks in the hash settings of parameters that are passed (model, response) as arguments.

In this case, you need to display the view in the success callback.

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

Obviously, from the previous answers, you know what you need to display on the callback with a successful fetch, however I think the problem is a bit bigger. Namely, your home route is used to create myModelView immediately, and not when loading its data. This is because you are calling render () to add to the body. Instead, try to initialize the view with an existing element. This way you can put off the call for rendering until the selection is complete:

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

Now you have not called render () yet, but your view has been successfully bound to the DOM as you planned.

+1
source

You can always set the default header using the "defaults" parameter: http://documentcloud.github.com/backbone/#Model-defaults .

0
source

Source: https://habr.com/ru/post/908101/


All Articles