【问题标题】:calling javascript on rendering views in BackBone js. post-render callback?在 BackBone js 中渲染视图时调用 javascript。渲染后回调?
【发布时间】:2012-02-04 23:48:22
【问题描述】:

看,主干视图渲染调用:

render: function() {
  $(this.el).html(this.template({title: 'test'}));  //#1
  this.renderScatterChart();
  return this;
},

所以,我将标准渲染称为#1。然后,我调用一个方法[这一次,它是图表库的包装器] 来寻找一个 div。 div 由 render 调用呈现。但此时,它还没有附加到 DOM(对吗?)。所以图表调用很遗憾地死了。

这是什么模式?我很想听到有一个渲染后回调。我已经尝试了一些技巧,有时我可以让图表工作,但事件没有绑定。

【问题讨论】:

    标签: javascript view backbone.js


    【解决方案1】:

    对于这类事情,我通常的做法是使用setTimeout,超时时间为零,以便在浏览器再次获得控制权后安排一些事情发生。试试这个:

    render: function() {
        $(this.el).html(this.template({title: 'test'}));
    
        var _this = this;
        setTimeout(function() {
            _this.renderScatterChart();
        }, 0);
    
        return this;
    }
    

    或者,如果renderScatterChart 已经绑定到相应的this

    render: function() {
        $(this.el).html(this.template({title: 'test'}));
        setTimeout(this.renderScatterChart, 0);
        return this;
    }
    

    如果您想更明确地说明您在做什么,也可以使用_.defer

    推迟 _.defer(function, [*arguments])

    延迟调用函数直到当前调用堆栈被清除,类似于使用延迟为0的setTimeout

    所以你也可以这样做:

    // Assuming that `renderScatterChart` is bound to the appropriate `this`...
    render: function() {
        $(this.el).html(this.template({title: 'test'}));
        _(this.renderScatterChart).defer();
        return this;
    }
    
    // or if it isn't bound...
    render: function() {
        $(this.el).html(this.template({title: 'test'}));
    
        var _this = this;
        _(function() {
            _this.renderScatterChart();
        }).defer();
    
        return this;
    }
    

    【讨论】:

    • @whatbird:这个setTimeout(..., 0) 技巧只是设置了一个函数,当你的JavaScript 完成并且控制权返回给浏览器时调用。因此,您的代码将设置所有内容并将所有内容添加到 DOM,然后浏览器接管并触发延迟功能。这个技巧有点小技巧,但很有用。
    • 如果在运行渲染之前将 view.el 插入 DOM,则不需要 hack。我不喜欢使用 setTimeout hack,遵循良好的流程永远不需要它。检查我的答案,解释在插入 .el 之前运行渲染和在插入之后运行的差异。一个运行崩溃,另一个运行正常,没有 setTimeout hack。 ;)
    • @DanielAranda:但这会迫使调用者了解视图的实现细节并用您的视图做非标准的事情。所以这是一个问题,你想要哪种组合(或者在当地情况下哪种组合是可能的)。
    • @muistooshort 我同意你,我知道在渲染之前插入 View 的元素也不是强制性的;我总是尽量避免使用 setTimeout,在这种情况下,我知道问题与 DOM 注入有关,我分享了一个示例,说明如何在不使用 setTimeout hack 的情况下修复它。
    • 这对我有用,但让我觉得不干净。也许带有渲染覆盖的 Backbone.Mixin 会更好地将这个 hack 集中在一个地方。
    【解决方案2】:

    我知道这是回答,但我想我会分享。 Underscore js 使用 _.defer 函数为您解决了这个问题。

    render: function() {
        $(this.el).html(this.template({title: 'test'}));  //#1
        _.defer( function( view ){ view.renderScatterChart();}, this );
        return this;
    },
    

    根据 Underscore 文档,这实际上与公认的解决方案相同。

    【讨论】:

      【解决方案3】:

      这是因为您在 .el 插入 DOM 之前运行了渲染。检查我的自我解释代码(在包含 Backbone.js 的空白页面中运行):

      function someThirdPartyPlugin(id){
         if( $('#' +id).length ===0  ){
          console.log('Plugin crashed');
         }else{
          console.log('Hey no hacks needed!!!'); 
         }
      }
      
      var SomeView = Backbone.View.extend({
          id : 'div1',
          render : function(){
            this.$el.append("<p>Hello third party I'm Backbone</p>");
            someThirdPartyPlugin( this.$el.attr('id') );
            return this;
        }
      }); 
      var SomeView2 = Backbone.View.extend({
          id : 'div2',
          render : function(){
            this.$el.append("<p>Hello third party I'm Backbone</p>");
            someThirdPartyPlugin( this.$el.attr('id') );
            return this;
        }
      }); 
      
      var myView1 = new SomeView();
      $('body').append( myView1.render().el );
      var myView2 = new SomeView2();
      $('body').append( myView2.el );
      myView2.render();
      

      【讨论】:

        【解决方案4】:

        您可以这样做(如果 renderSactterChart 对“jQuery 化”对象进行操作):

        render: function() {
          this.$el.html(this.template({title: 'test'}));  //#1
          this.$el.renderScatterChart();
          return this;
        },
        

        (这不是真正的 DOM 元素...)

        【讨论】:

          【解决方案5】:

          遇到了和你一样的问题……使用 highcharts。我使用的是 Backbone.LayoutManager,最终破解了代码以添加 postRender 回调。希望将其视为 Backbone.js 的一部分

          【讨论】:

            【解决方案6】:

            如果您不想使用setTimeout hack,我认为这种方式更“正常”:

            只需将$el元素传递给需要操作render()添加的元素的函数,然后就可以对$el进行DOM操作了。

            【讨论】:

              【解决方案7】:

              这个问题已经得到解答,但为了以后的参考,我已经遇到过几次这个问题。我通过添加自定义事件来解决它,在这种情况下它可能类似于

              render: function() {
                $(this.el).html(this.template({title: 'test'}));  //#1
              
                this.on('someView:append', function() {
                    this.renderScatterChart();
                }, this);
              
                return this;
              }
              

              然后,当我将元素附加到 DOM 时,我会像这样触发它

              myView.trigger('someView:append');
              

              现在,这肯定不是一个完美的解决方案。您正在将触发此事件的责任添加到将呈现的视图附加到 DOM 的任何内容中。根据您的代码结构如何,它可以更好或更差。同样,我只是将其发布为将来的参考和替代方案。

              更多信息:http://backbonejs.org/#Events

              【讨论】:

                【解决方案8】:

                应该这样做:

                render: function() {
                  Marionette.ItemView.prototype.render.apply(this, arguments);
                  your code ...
                }
                

                木偶样本,但想法也适用于主干视图

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2016-11-13
                  • 1970-01-01
                  • 2023-03-11
                  • 1970-01-01
                  • 1970-01-01
                  • 2013-01-10
                  相关资源
                  最近更新 更多