【问题标题】:How to ensure that Backbone has rendered the page completely?如何确保 Backbone 已经完全渲染了页面?
【发布时间】:2013-05-09 05:54:12
【问题描述】:

我使用 BackboneJS 开发大型企业应用程序。应用程序中的一个页面是通过 REST 使用多个子系统调用构建的。如何确保已调用加载页面所需的所有服务并且已完成模板绑定?

例如,我有一个MasterView,它负责每个子视图的collection.fetch(),就像这样。

myApp.views.MasterView = Backbone.View.extend({
    initialize: function(params) {
        var self = this;
        this.collection.fetch({
            success: function(resp) {
                self.collection.bind("reset", self.render(), self);
            },
            error: function(xhr, xhrStatus) {
                // push error message, in case of fetch fails.
            }
        });
    },
    render: function() {
        var self = this;
        this.collection.each(function(model) {
            if (model.get('authorize') && model.get('status') === "success" && model.get('data').length > 0) {
                self.bindTemplate(model.get('data')[0]);
            }
        });
    }
});

我为页面设置了一个视图,它负责呈现另外两个视图 CustomerInfo 和 CustomerAccounts。视图是这样的。

myApp.views.CustomerView = Backbone.View.extend({
        initialize: function() {
            var customerInfo = new myApp.collection.CustomerInfo();
            new myApp.views.CustomerInfo({el: $("#infoContainer"), collection: customerInfo});

            var customerAccount = new myApp.collection.CustomerAccount();
            new myApp.views.CustomerAccount({el: $("#accountContainer"), collection: customerAccount});
        }
});

而 CustomerInfo 和 CustomerAccount 视图,看起来像这样,

myApp.views.CustomerInfo = myApp.views.MasterView.extend({
    initialize: function() {
        var self = this;
        myApp.views.MasterView.prototype.initialize.call(self, {
            qParam1: "qparam1",
            qParam2: "qparam2"
        });
    },
    render: function() {
        var self = this;
        self.template = _.template(myApp.Templates.get("customer-page/customer-info"));
        myApp.views.MasterView.prototype.render.apply(self);
    },
    bindTemplate: function(data) {
        var self = this;
        $(self.el).html(self.template({"info": data}));
    }
});
 
myApp.views.CustomerAccounts = myApp.views.MasterView.extend({
    initialize: function() {
        var self = this;
        myApp.views.MasterView.prototype.initialize.call(self, {
            qParam1: "qparam1"
        });
    },
    render: function() {
        var self = this;
        self.template = _.template(myApp.Templates.get("customer-page/customer-accounts"));
        myApp.views.MasterView.prototype.render.apply(self);
    },
    bindTemplate: function(data) {
        var self = this;
        $(self.el).html(self.template({"accounts": data}));
    }
});

我想知道是否有任何方法可以从myApp.views.CustomerView 知道视图CustomerInfoCustomerAccounts 已经完成渲染?我在这里遇到的主要问题是CustomerInfo 视图加载速度很快,但CustomerAccount 视图需要一些时间才能加载。因此,当两个视图都在 DOM 上准备好时,我需要一次显示页面。

【问题讨论】:

  • 只是一个观察:您的子视图确实不应该引用父视图(我实际上进一步争辩说,任何视图都不应该引用另一个视图,但这完全是另一个话题)我会还建议阅读一些关于如何更有效地使用 Backbone 的教程/示例;你的观点应该对模型/集合的变化做出反应;就目前而言,您的观点做得太多了。

标签: backbone.js views rendering backbone-views viewrendering


【解决方案1】:

当你实例化你的视图时,添加一个监听器到 view_ready 事件。当视图完成获取数据并渲染时,它会自行触发它 在父视图的render方法结束时

self.trigger('view_ready');

然后在 Main 视图中添加如下内容:

this.listenTo(CustomerInfoView, 'view_ready', this.customer_info_ready);
this.listenTo(CustomerAccountsView, 'view_ready', this.customer_account_ready);

然后在您的主视图或主模型中添加 2 个属性:info_ready 和 customer_ready 并将它们初始化为 0 每次触发前面提到的 2 个事件之一时,请执行以下操作:

customer_info_ready : function(){
    this.model.set('info_ready',true);
    if (this.model.get('account_ready') === true) {
         this.trigger('both_elements_ready'); 
     }
}
customer_account_ready : function(){
    this.model.set('account_ready',true);
    if (this.model.get('info_ready') === true) {
         this.trigger('both_elements_ready'); 
     }
}

然后在你的主视图上为'both_elements_ready'添加一个监听器:

initialize: function() { 
 //your code
 this.on('both_elements_ready',this.page_ready); }

编辑:添加信息以使答案与问题更相关且更详细。

【讨论】:

  • 感谢您的回复,但很抱歉我在第一次发布时无法完成我的确切要求。根据您的建议,我会知道 CustomerInfoViewCustomerAccountsView 何时准备好,但我需要知道它们何时准备好。
  • 哇,这比问题还要复杂。如果您计划解决它并使其比最初没有框架时更复杂,那么使用框架有什么意义?
  • @RakanNimer:如果问题中提到的只有 2 个视图,您的解决方案可能会很好。但是,如果myApp.views.CustomerView里面有更多的视图呢?
  • @Pierreten 你建议做什么?
  • 好吧,使用 ChaplinJS 会以非常优雅的方式解决这个问题!
【解决方案2】:

编辑:这个答案的灵感来自我从示例“与卓别林一起吃早午餐”示例here 中学到的东西。 Chaplin 是建立在主干之上的框架。


好的,所以我将建议一个基于coffeescript的解决方案(我恰好在coffee中有一个解决方案:/!如果您想转换回js,请尝试使用js2coffee

大师班

我们的想法是有一个大师班——而不是视图——它将把应用程序放在手中,而不是主视图。

module.exports = class Application
  initialize: (finished_init) =>
    @collection = new Collection()
    @main_view = new View()
    @collection.fetch(
      success: (collection) =>
        @main_view.collection = @collection
        finished_init()
    )
  render: (targetLocation, params) ->
    switch targetLocation
      when "main" then (
        @main_view.render()
      )

initialize 方法中,我们获取集合数据。成功时,finshed_init() 被调用。您可以将其替换为 @render(顺便说一句,@ == this :))

初始化

这是我初始化应用程序的方式:

$ ->
  app = new Application()

  app.initialize( ->
    #----------------------------------#
    # Create the router and its routes
    # This is called at the finished_init() of the Application class
    #----------------------------------#
    app_router = new Router
  )

异步运行多个fetche

你可以有一个监控 fetche 完成的函数,或者你可以尝试使用async。它有一个很好的 `parallel 函数可以做到这一点。

@collection1 = new Collection()
@collection = new Collection()
@main_view1 = new View()
@main_view2 = new View()

async.parallel [ (callback) ->
  setTimeout (->
    @collection1.fetch(
      success: (collection) =>
        @main_view1.collection = @collection
        callback null, "one"
  ), 200
, (callback) ->
  setTimeout (->
    @collection2.fetch(
      success: (collection) =>
        @main_view2.collection = @collection
        callback null, "two"
  ), 100
 ], (err, results) ->
   if not err
    @render()  # Render what you want

【讨论】:

    【解决方案3】:

    经过一段时间的思考,在谷歌上搜索,我找到了this link

    所以我对我的 MasterView 进行了一些更改,它正在工作并解决了我遇到的问题。我在 MasterView 中所做的更改是这样的

    var activeConnections=0;
    
    myApp.views.MasterView = Backbone.View.extend({
        initialize: function(params) {
    
            activeConnections++;
    
            var self = this;
            this.collection.fetch({
                success: function(resp) {
    
                    activeConnections--;
    
                    self.collection.bind("reset", self.render(), self);
    
                    if(activeConnections===0){
                        // trigger the page has finished rendering
                    }
                },
                error: function(xhr, xhrStatus) {
                    // push error message, in case of fetch fails.
                    if(activeConnections===0){
                        // trigger the page has finished rendering
                    }
                }
            });
        },
        render: function() {
            var self = this;
            this.collection.each(function(model) {
                if (model.get('authorize') && model.get('status') === "success" && model.get('data').length > 0) {
                    self.bindTemplate(model.get('data')[0]);
                }
            });
        }
    });
    

    感谢所有帮助我解决这个问题的人。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-08-20
      • 1970-01-01
      • 2015-02-25
      • 2015-06-04
      • 2016-03-24
      • 2023-01-02
      • 1970-01-01
      • 2011-03-19
      相关资源
      最近更新 更多