【问题标题】:backbone.js view renders before model fetch模型获取之前的主干.js 视图呈现
【发布时间】:2013-11-08 22:42:14
【问题描述】:

我正在尝试制作一个小型的backbone.js 应用程序,但在事情的顺序上遇到了困难。

在我的 html 文件中,标题中有两个脚本块:

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

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

在我的 app.js 中,我定义了一个简单的模型、视图和一个路由器。

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

该应用程序由一个简单的 sinatra 应用程序提供服务。 url /mymodel 提供静态 json 文件:

{
    "title": "My Model",
}

加载应用程序时,我在 javascript 控制台中收到错误消息:

Uncaught ReferenceError: title is not defined

问题似乎是,视图在从服务器获取模型之前呈现自身。这是为什么呢?

昨天,我关注了来自 PeepCode 的前两个主干.js 截屏视频。我试图将我的应用程序与截屏视频中的应用程序进行比较,但看不出我的应用程序想要工作的原因。

有什么建议吗?

【问题讨论】:

  • 我花了好几个小时...... 好几个小时才弄清楚这就是为什么我的应用程序无法运行,有时无法运行,或者在某些浏览器上运行而不在其他浏览器上运行的原因。当您考虑 fetch 应该如何工作时,我想这是一个新手错误。学过的知识。我认为有太多的 Backbone 示例使用硬编码的数据和非常重要的概念,比如这个被搁置了。

标签: backbone.js


【解决方案1】:

在这种情况下,您应该引导您的模型数据,以便在页面加载时可用。

代替

window.model.fetch();

把这样的东西放进去(如果使用 .erb)

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

否则,您需要在获取模型后渲染视图,例如

绑定视图以在模型更改时渲染

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

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

或者处理model.fetch成功并渲染视图

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

【讨论】:

  • 这个答案让我最接近解决方案。我在我的路由器中删除了对渲染的调用,但保留了将视图“el”属性添加到文档的代码。因此,当 fetch 完成时,它会更改我的模型,这会引发 change 事件,该事件会调用 render 方法。
【解决方案2】:

您还可以利用Backbone.Model.fetch() 返回的延迟对象http://api.jquery.com/category/deferred-object/,如下所示:

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

您可能想要创建一个骨干模型的扩展,在您的模型上存储一个您可以引用的延迟对象,如下所示:

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

然后在你的视图渲染方法中,你可以这样说:

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

使用扩展模型并在整个主干应用程序中遵循此模式非常方便。

【讨论】:

    【解决方案3】:

    从服务器重置模型的状态。如果模型有 从未填充过数据,或者如果您想确保您 拥有最新的服务器状态。一个“改变”事件将被触发,如果 服务器的状态与当前属性不同。接受 选项哈希中的成功和错误回调,它们被传递 (模型,响应)作为参数。

    在这种情况下,您需要在成功回调中呈现视图。

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

    【讨论】:

      【解决方案4】:

      很明显,从前面的答案中,您知道您需要在获取成功回调上进行渲染,但是我认为您的问题不止于此。即,您的主路由用于立即构建 myModelView,而不是在其数据加载时。发生这种情况是因为您正在调用 render() 以附加到正文。相反,尝试使用现有元素初始化视图。这样您就可以延迟渲染调用,直到 fetch 完成:

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

      现在,您还没有调用 render(),但您的视图已按预期成功附加到 DOM。

      【讨论】:

      • 那么在这里,您在初始化和主页函数中都创建了 MyModelView 的实例?还是应该从初始化中删除它?
      • 你是对的,从初始化中删除它。对困惑感到抱歉。无论如何,将它放在初始化中并不总是有意义的,因为通常您将根据调用的路由方法呈现不同的视图。
      【解决方案5】:

      您始终可以使用“默认”选项设置默认标题:http://documentcloud.github.com/backbone/#Model-defaults

      【讨论】:

      • 是的,但是你渲染一个带有错误数据的视图。 :P
      猜你喜欢
      • 1970-01-01
      • 2012-01-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-17
      • 2013-04-15
      相关资源
      最近更新 更多