在呈现Backbone View时收到通知

时间:2012-09-05 16:30:54

标签: javascript backbone.js coffeescript backbone-views

我正在研究Backbone.View,它应该将集合呈现为可滚动列表。

初始渲染的一部分,我需要访问一些布局属性(例如clientWidth),这些属性仅在渲染视图后才可用。

我的问题是,如何知道何时将视图添加到DOM?


使用Backbone.View通常有两种方法将视图附加到DOM:

  1. 创建视图>渲染它>附上它:

    view = new MyList().render()
    $('#dummy').append(view.$el)
    
  2. 创建视图并就地呈现:

    new MyList({el: '#dummy'}).render()
    
  3. 注意:我知道(1)和(2)不完全等效,这不是重点。


    让我们考虑我的列表定义如下:

    class MyList extends Backbone.View
        render: ->
            @$el->html( ... )
            @
    
        layout: ->
            max = $el.scrollWidth - $el.clientWidth
            # ... disable / enable scrolling arrows based on max ...
    

    在MyList附加到DOM之后,您如何确保调用layout()?

3 个答案:

答案 0 :(得分:8)

我遇到过这个问题,并且有些模式可以提供帮助。 Backbone为您提供工具,但不会告诉您如何使用它们。在使用它时建立良好的模式非常重要。我使用了两种不同的模式来帮助解决这个问题。

1)根据您的应用程序的工作方式,可能很容易或很难确保在渲染和附加视图后始终调用attached()方法或触发attached事件它到DOM。就我而言,这很简单,因为我创建了一个架构,我可以在一个中心位置处理它。

2)如果你总是在渲染之后立即将你的元素附加到DOM - 特别是在渲染之后的任何时候没有通过在异步回调中这样做而延迟附着,那么在你的render()函数中你可以将调用推迟到{{ 1}}或附加后需要运行的任何东西。像这样:

layout()

Javascript始终是单线程的。 I / O可以异步完成,但实际需要JS引擎时间的任何东西都不能与其他JS代码并行运行。当您在没有最后一个参数或值为var MyList = Backbone.View.extend({ render: function() { /* rendering logic goes here */ // Notice that the timeout is 0 - you can actually not even pass this argument // but I wanted to emphasize that there is no delay given. setTimeout(this.layout, 0); return this; }, layout: function() { // This code won't be run until after this.el is attached to the DOM } }); var view = new MyList().render(); $('#dummy').append(view.$el); 的情况下调用setTimeout()时,它将基本上将其排队,以便在当前执行的代码完成后尽快运行。

如果你在调用0之后不久就遵循一个总是附加到DOM的模式,那么你可以利用这个事实 - 在当前执行代码完成运行之前的任何时候将控制权返回给JS引擎运行任何东西排在下一个排队。当运行render()排队的功能时,您就可以知道您的视图已附加到DOM。请注意,这与_.defer()的内容相同,但我想为您解释一下。

再一次,模式非常重要。如果你从来没有保持一致并且没有在你的应用程序中建立模式,你就没有任何保证,你就失去了依赖这种机制的能力 - 以及你可能能够建立的其他机制。希望这有帮助!

答案 1 :(得分:3)

这是一个看似困难的问题。有DOM Mutation Events,但AFAIK这些都没有跨浏览器实现,无论如何都被弃用了。 Mutation Observers似乎是新的方式,但是,我不确定兼容性。

一个万无一失,但是昂贵/凌乱的做法是轮询以确定身体是否在祖先链中的某个地方。

whenInserted = function ($el, callback) {
  check = function () {
    if ($el.closest('body').length) { // assuming a jQuery object here
      callback();
    } else {
      setTimeout(check, 100);
    }
  };
  check();
};

答案 2 :(得分:0)

我很惊讶这些答案有多糟糕。 JQuery方法是同步的。

所以在Backbone.View渲染功能中你有一些不错的选择:

//只需调用一个函数

  render:function(){

          //first, use Backbone/jQuery to append to DOM here

           //second 

   this.callSomeFunctionToSignifyRenderIsDone();

      return this;
    }

//触发事件

render:function(){

   //use Backbone/jQuery to append to DOM here


   Backbone.Events.trigger('viewX-rendered'.); //trigger an event when render is complete

  return this;
}

或者,万一你的render方法有异步代码,你可以将一个可选的回调函数传递给render函数:

render: function(callback){

   //use Backbone/jQuery to append to DOM here

  if(typeof callback === 'function'){
     callback(null,'viewX-rendered'); //invoke the error-first callback
}
}

考虑到返回'这个'从渲染到链骨干调用是首选模式,然后前两个选项可能是更好的选择。

最后,如果您当前的渲染方法确实是异步的,也就是说,将东西附加到DOM的jQuery代码发生异步,那么您可以使用jQuery将侦听器附加到DOM元素,如此

https://gist.github.com/azat-co/5898111