【问题标题】:Computed property based on array values is not recomputed不会重新计算基于数组值的计算属性
【发布时间】:2012-11-14 15:04:10
【问题描述】:

问题空间

我正在渲染一些嵌套的 Ember 视图,以便制作拆分窗格样式的 UI。我想在第一次渲染时调整我的视图大小,以便它们具有相等的宽度。我不希望我的子视图互相看着,所以我使用 Ember.ContainerView 的子类来保存我的内容和可拖动的拆分器句柄。

我不能在我的容器视图上使用Ember.View#didInsertElement,因为我需要等待我的子视图完全呈现。

我的(尝试过的)解决方案

我正在使用此答案中提供的代码:How to wait for a template to be fully rendered。这会将属性isRendered 添加到所有Ember.View 实例,当模板通过重新打开Ember.View 触发didInsertElement 时自动设置该属性:

Ember.View.reopen

  didInsertElement: ->
    res = @_super();
    @_setIsRendered();
    res

  _setIsRendered: ->
    if (!! @$())
      @set('isRendered', true)
    else
      Ember.run.next this, ->
        @_setIsRendered()

我尝试重新打开 Ember.ContainerView 以将 childViewsRendered 属性添加到所有容器视图,但 Ember 反对并为子视图中只有一项的容器视图抛出了一些非常奇怪的 IndexOutOfBounds 错误。

我最终将我的收藏代码放在以下 mixin 中:

App.ChildrenRendered = Ember.Mixin.create

  childViewsRendered: (->
    res = @get('childViews').everyProperty('isRendered')
    console.log('childViewsRendered', res, this)

    # Pointer to this most offensive object for debugging
    window.wtf = this
    res
  ).property('childViews.@each.isRendered')

  _runChildViewsDidRender: (->
    if @get('childViewsRendered')
      console.log('trying to invoke childViewsDidRender')
      Ember.tryInvoke(this, 'childViewsDidRender')
  ).observes('childViewsRendered')

然后我有一个这样的课程:

App.SplitterView = Ember.ContainerView.extend App.ChildrenRendered,
  # ...(some properties)...
  init: ->
    child_views = @get('childViews')
    child_views.pushObjects([App.WindowView.create(), App.WindowView.create()])

什么有效:

  • App.SplitterView#childViewsRendered 在任何视图呈现之前计算一次,因此变为 false
  • 视图由 Ember 处理(插入和渲染),并设置它们自己的 isRendered 属性。
  • 稍后运行 window.wtf.get('childViews').everyProperty('isRendered') 返回 true

什么不起作用:

  • 计算属性 childViewsRendered 永远不会再自我更新。
  • childView 数组元素成员上的虚拟值的计算属性似乎也不起作用。

【问题讨论】:

    标签: javascript coffeescript ember.js


    【解决方案1】:

    如果没有 jsFiddle 看,我只能按照您所说的和您提供的 sn-ps 进行操作,但似乎您要做的只是确保容器的所有 childViews 都在在做某事之前的DOM,是吗?

    如果是这样的话...当视图被添加到childViews 时,正如您所知,它们会自动呈现并插入到 DOM 中。好吧,这一切都发生在同一个 RunLoop 中。因此,要延迟某些函数的执行,直到所有子级都“inDOM”,就像观察childViews.@each 并使用Ember.run.next 将执行延迟到下一个RunLoop 一样简单。这是一个例子

    App.SomeContainerView = Em.ContainerView.extend
      init: ->
        @_super() # run default init
        # add 2 of our 4 views
        @get('childViews').pushObjects [@aView.create(), @bView.create()]
    
      # a non-cached (volatile) computed property to check if children are inDOM
      # just used for demo purposes
      childrenAreInDom: (->
        @get('childViews').every (v) ->
          v.state is "inDOM"
      ).property().volatile()
    
      # our observer
      observeChildren: (->
        # if the container isn't inDOM yet the afterRender function we add below 
        # will handle it, if views are added/removed after that then we handle it here
        return unless @state is "inDOM"
        # output to console if every childview are inDOM
        console.log "observeChildren", @get('childrenAreInDom')
        # the above will always be false since we're still in the same RunLoop
        # next RunLoop our function will run
        Ember.run.next @, 'doWhatever'
      ).observes('childViews.@each') # observe changes in the childViews array
    
      afterRender: ->
        console.log "afterRender", @get('childrenAreInDom')
        Ember.run.next @, 'doWhatever'
    
      # the function we want to run eventually after children are inDOM
      doWhatever: ->
        console.log "doWhatever"
        # print out childrenAreInDom one more time.. it will be true
        console.log "childrenAreInDom:", @get('childrenAreInDom')
    
      # some views to insert
      aView: Em.View.extend
        template: Em.Handlebars.compile("A")
      bView: Em.View.extend
        template: Em.Handlebars.compile("B")
      cView: Em.View.extend
        template: Em.Handlebars.compile("C")
      dView: Em.View.extend
        template: Em.Handlebars.compile("D")
    

    如果您要在模板中使用 {{view App.SomeContainerView}},您会在控制台中看到:

    afterRender false
    doWhatever
    childrenAreInDom true 
    

    如果您在容器已经在 DOM 中之后以编程方式通过 pushObjects 添加 cView 和 dView,您会看到

    observeChildren false
    doWhatever
    childrenAreInDom true 
    

    即使这不是你想要的,希望它可以帮助你到达你需要的地方,而不需要所有 Mixin 废话:D

    【讨论】:

      猜你喜欢
      • 2020-02-05
      • 2018-07-11
      • 1970-01-01
      • 2018-01-22
      • 2015-01-28
      • 2016-11-10
      • 1970-01-01
      • 2020-12-10
      • 1970-01-01
      相关资源
      最近更新 更多