【发布时间】:2012-11-25 11:49:06
【问题描述】:
有许多问题以一种或另一种方式提出:“在呈现视图的某些部分后,我该如何做某事?” (here、here 和 here 仅举几例)。答案通常是:
- 使用
didInsertElement在视图最初呈现时运行代码。 - 使用
Ember.run.next(...)运行您的代码在视图更改被刷新后,如果您需要访问已创建的 DOM 元素。 - 在您需要的数据加载后,使用
isLoaded或类似属性上的观察者来执行某些操作。
令人恼火的是,它会导致一些看起来非常笨拙的东西:
didInsertElement: function(){
content.on('didLoad', function(){
Ember.run.next(function(){
// now finally do my stuff
});
});
}
当您使用 ember-data 时,这甚至不一定有效,因为isLoaded 可能已经为真(如果之前已经加载了记录并且不再从服务器请求)。所以要正确排序是很困难的。
最重要的是,您可能已经在您的视图模板中观看 isLoaded,如下所示:
{{#if content.isLoaded}}
<input type="text" id="myTypeahead" data-provide="typeahead">
{{else}}
<div>Loading data...</div>
{{/if}}
在你的控制器中再做一次似乎是重复。
我想出了一个稍微新颖的解决方案,但它要么需要工作,要么实际上是个坏主意……这两种情况都可能是真的:
我编写了一个名为 {{fire}} 的小型 Handlebars 助手,它会在执行包含的把手模板时触发一个具有自定义名称的事件(即,每次重新渲染子视图时都应该这样,对吗?)。
这是我的非常早期尝试:
Ember.Handlebars.registerHelper('fire', function (evtName, options) {
if (typeof this[evtName] == 'function') {
var context = this;
Ember.run.next(function () {
context[evtName].apply(context, options);
});
}
});
这样使用:
{{#if content.isLoaded}}
{{fire typeaheadHostDidRender}}
<input type="text" id="myTypeahead" data-provide="typeahead">
{{else}}
<div>Loading data...</div>
{{/if}}
这基本上是按原样工作的,但它有几个我已经知道的缺陷:
- 它调用控制器上的方法...至少能够将“事件”发送到祖先视图对象可能会更好,甚至可能将其设为默认值行为。我试过
{{fire typeaheadHostDidRender target="view"}},但没有奏效。我还看不到如何从传递给助手的内容中获取“当前”视图,但显然{{view}}助手可以做到。 - 我猜有比我在这里做的更正式的方法来触发自定义事件,但我还没有学会。 jQuery 的
.trigger()似乎不适用于控制器对象,尽管它可能适用于视图。有没有“Ember”的方式来做到这一点? - 可能有些事情我不明白,例如会触发此事件但视图实际上并未添加到 DOM...?
正如您可能猜到的,我正在使用 Bootstrap 的 Typeahead 控件,并且我需要在呈现 <input> 之后连接它,这实际上只发生在我的模板中几个嵌套的 {{#if}} 块评估为 true 之后.我也使用 jqPlot,所以我经常需要这种模式。这似乎是一个可行且有用的工具,但可能是我遗漏了一些使这种方法变得愚蠢的大图景。或者,我的搜索中没有显示其他方法?
有人可以为我改进这种方法或告诉我为什么这是一个坏主意吗?
更新
我已经弄清楚了一些问题:
- 我可以使用
options.data.view.get('parentView')获得第一个“真实”包含视图...也许很明显,但我认为它不会那么简单。 - 实际上你可以在任意对象上做一个jQuery风格的
obj.trigger(evtName)...但是这个对象必须扩展Ember.Eventedmixin!所以我认为这是在 Ember 中发送这种事件的正确方法。只需确保预期目标扩展Ember.Evented(视图已经完成)。
这是目前的改进版本:
Ember.Handlebars.registerHelper('fire', function (evtName, options) {
var view = options.data.view;
if (view.get('parentView')) view = view.get('parentView');
var context = this;
var target = null;
if (typeof view[evtName] == 'function') {
target = view;
} else if (typeof context[evtName] == 'function') {
target = context;
} else if (view.get('controller') && typeof view.get('controller')[evtName] == 'function') {
target = view.get('controller');
}
if (target) {
Ember.run.next(function () {
target.trigger(evtName);
});
}
});
现在我所缺少的只是弄清楚如何传入预期的目标(例如控制器或视图——上面的代码试图猜测)。或者,找出是否存在破坏整个概念的意外行为。
还有其他输入吗?
【问题讨论】:
-
作为旁注,我意识到另一种方法是为每个子视图显式定义视图对象和模板......这样你就可以显式地观看每个子视图上的
didInsertElement事件.但这似乎真的有点矫枉过正,只是为了其他只需要{{#if isLoaded}}才能工作的东西。但是,如果“子视图”更复杂,这可能是一个更好的选择。
标签: ember.js