问题空间
我正在渲染一些嵌套的 Ember View ,这样我就可以制作一个拆分 Pane 样式的 UI。我想在第一次渲染时调整我的 View 大小,以便它们具有相同的宽度。我不希望我的 subview 相互查看,所以我使用 Ember.ContainerView
的子类来保存我的内容和可拖动的拆分器句柄。
我不能在我的容器 View 上使用 Ember.View#didInsertElement
,因为我需要等待我的 subview 完全呈现。
我的(尝试过的)解决方案
我正在使用此答案中提供的代码: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
属性添加到所有容器 View ,但 Ember 反对并为容器 View 抛出一些非常奇怪的 IndexOutOfBounds 错误childViews 中的一项。
我最终将我的收集代码放在了下面的 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
在任何 View 呈现之前计算一次,因此变为false
- View 由 Ember 处理(插入和渲染),并设置它们自己的
isRendered
属性非常好。 - 稍后运行
window.wtf.get('childViews').everyProperty('isRendered')
返回true
。
什么不起作用:
- 计算属性
childViewsRendered
永远不会再自行更新。 childView
数组元素成员的虚拟值的计算属性似乎也不起作用。
最佳答案
如果没有 jsFiddle,我只能根据您所说的内容和您提供的片段进行操作,但看起来您要做的只是确保容器的所有 subview 之前都在 DOM 中做某事,是吗?
如果是这种情况...当 View 被添加到 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
关于javascript - 不重新计算基于数组值的计算属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13576675/