有许多问题以这样或那样的方式提出:"在呈现视图的某些部分之后我该怎么做?" (这里,这里,这里只是为了给几个).答案通常是:
didInsertElement在最初呈现视图时运行代码.Ember.run.next(...)运行代码,如果需要访问创建的DOM元素.isLoaded后,使用观察者或类似属性执行某些操作.令人恼火的是,这导致了一些非常笨拙的样子:
didInsertElement: function(){
content.on('didLoad', function(){
Ember.run.next(function(){
// now finally do my stuff
});
});
}
Run Code Online (Sandbox Code Playgroud)
当你使用ember-data时,这实际上并不一定有用,因为isLoaded可能已经是真的(如果记录之前已经加载过,并且不再从服务器请求).因此,正确排序是很困难的.
最重要的是,您可能已经在视图模板中观看isLoaded,如下所示:
{{#if content.isLoaded}}
<input type="text" id="myTypeahead" data-provide="typeahead">
{{else}}
<div>Loading data...</div>
{{/if}}
Run Code Online (Sandbox Code Playgroud)
并在您的控制器中再次执行它似乎是重复.
我提出了一个有点新颖的解决方案,但它要么需要工作,要么实际上是一个坏主意......任何一种情况都可能是真的:
我写了一个小的Handlebars帮助器{{fire}},当执行包含的把手模板时会触发一个带有自定义名称的事件(即应该每次重新渲染子视图时,对吗?).
这是我很早期的尝试:
Ember.Handlebars.registerHelper('fire', function (evtName, options) {
if (typeof this[evtName] == 'function') {
var context = this;
Ember.run.next(function () {
context[evtName].apply(context, options);
});
}
});
Run Code Online (Sandbox Code Playgroud)
使用方式如下:
{{#if content.isLoaded}}
{{fire typeaheadHostDidRender}}
<input type="text" id="myTypeahead" data-provide="typeahead">
{{else}}
<div>Loading data...</div>
{{/if}}
Run Code Online (Sandbox Code Playgroud)
这本质上是按原样工作的,但它有一些我已经知道的缺陷:
{{fire typeaheadHostDidRender target="view"}},但没用.我还不知道如何从传递给帮助器的内容中获取"当前"视图,但显然{{view}}帮助程序可以执行此操作..trigger()似乎不适用于控制器对象,尽管它可能适用于视图.是否有"Ember"方式来做到这一点?正如你可能猜到的那样,我正在使用Bootstrap的Typeahead控件,我需要在<input>渲染后连接它,这实际上只发生{{#if}}在我的模板中几个嵌套块评估为true之后.我也使用jqPlot,所以我经常遇到这种模式的需要.这似乎是一个可行且有用的工具,但它可能是我错过了一些让这种方法变得愚蠢的大局.或者也许有另一种方法可以做到这一点,但我的搜索中没有显示出来?
有人可以为我改进这种方法或告诉我为什么这是一个坏主意?
我已经找到了一些比特:
options.data.view.get('parentView')...的视图,或许显而易见,但我不认为它会那么简单.obj.trigger(evtName)在任意对象上做一个jQuery风格......但是对象必须扩展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);
});
}
});
Run Code Online (Sandbox Code Playgroud)
现在几乎所有我都缺少的是弄清楚如何传入预定的目标(例如控制器或视图 - 上面的代码试图猜测).或者,弄清楚是否存在一些违背整个概念的意外行为.
还有其他输入吗?
S'p*_*'Kr 16
更新了Ember 1.0 final,我目前在Ember 1.3.1上使用此代码.
好吧,我想我已经弄明白了.这是"完整"的车把帮手:
Ember.Handlebars.registerHelper('trigger', function (evtName, options) {
// See http://stackoverflow.com/questions/13760733/ember-js-using-a-handlebars-helper-to-detect-that-a-subview-has-rendered
// for known flaws with this approach
var options = arguments[arguments.length - 1],
hash = options.hash,
hbview = options.data.view,
concreteView, target, controller, link;
concreteView = hbview.get('concreteView');
if (hash.target) {
target = Ember.Handlebars.get(this, hash.target, options);
} else {
target = concreteView;
}
Ember.run.next(function () {
var newElements;
if(hbview.morph){
newElements = $('#' + hbview.morph.start).nextUntil('#' + hbview.morph.end)
} else {
newElements = $('#' + hbview.get('elementId')).children();
}
target.trigger(evtName, concreteView, newElements);
});
});
Run Code Online (Sandbox Code Playgroud)
我将名称从{{fire}}更改{{trigger}}为更接近Ember.Evented/jQuery约定.此更新的代码基于内置的Ember {{action}}帮助程序,并且应该能够接受target="..."模板中的任何参数,就像{{action}}这样.与它不同的地方{{action}}(除了在渲染模板部分时自动触发):
actions: {…}哈希中调用某些内容!)请注意,如果您将事件发送到Ember.View实例,您所要做的就是使用相同的名称实现一个方法(请参阅文档,代码).但是,如果您的目标不是视图(例如控制器),则必须在对象obj.on('evtName', function(evt){...})或Function.prototype.on扩展名上注册侦听器.
所以这是一个现实世界的例子.我有一个使用以下模板的视图,使用Ember和Bootstrap:
<script data-template-name="reportPicker" type="text/x-handlebars">
<div id="reportPickerModal" class="modal show fade">
<div class="modal-header">
<button type="button" class="close" data-dissmis="modal" aria-hidden="true">×</button>
<h3>Add Metric</h3>
</div>
<div class="modal-body">
<div class="modal-body">
<form>
<label>Report Type</label>
{{view Ember.Select
viewName="selectReport"
contentBinding="reportTypes"
selectionBinding="reportType"
prompt="Select"
}}
{{#if reportType}}
<label>Subject Type</label>
{{#unless subjectType}}
{{view Ember.Select
viewName="selectSubjectType"
contentBinding="subjectTypes"
selectionBinding="subjectType"
prompt="Select"
}}
{{else}}
<button class="btn btn-small" {{action clearSubjectType target="controller"}}>{{subjectType}} <i class="icon-remove"></i></button>
<label>{{subjectType}}</label>
{{#if subjects.isUpdating}}
<div class="progress progress-striped active">
<div class="bar" style="width: 100%;">Loading subjects...</div>
</div>
{{else}}
{{#if subject}}
<button class="btn btn-small" {{action clearSubject target="controller"}}>{{subject.label}} <i class="icon-remove"></i></button>
{{else}}
{{trigger didRenderSubjectPicker}}
<input id="subjectPicker" type="text" data-provide="typeahead">
{{/if}}
{{/if}}
{{/unless}}
{{/if}}
</form>
</div>
</div>
<div class="modal-footer">
<a href="#" class="btn" data-dissmis="modal">Cancel</a>
<a href="#" {{action didSelectReport target="controller"}} class="btn btn-primary">Add</a>
</div>
</div>
</script>
Run Code Online (Sandbox Code Playgroud)
我需要知道这个元素何时在DOM中可用,所以我可以附加一个预先输入:
<input id="subjectPicker" type="text" data-provide="typeahead">
Run Code Online (Sandbox Code Playgroud)
所以,我{{trigger}}在同一个区块中放了一个帮手:
{{#if subject}}
<button class="btn btn-small" {{action clearSubject target="controller"}}>{{subject.label}} <i class="icon-remove"></i></button>
{{else}}
{{trigger didRenderSubjectPicker}}
<input id="subjectPicker" type="text" data-provide="typeahead">
{{/if}}
Run Code Online (Sandbox Code Playgroud)
然后didRenderSubjectPicker在我的视图类中实现:
App.ReportPickerView = Ember.View.extend({
templateName: 'reportPicker',
didInsertElement: function () {
this.get('controller').viewDidLoad(this);
}
,
didRenderSubjectPicker: function () {
this.get('controller').wireTypeahead();
$('#subjectPicker').focus();
}
});
Run Code Online (Sandbox Code Playgroud)
完成!现在,当最终渲染模板的子部分时(并且仅在何时),变量才会被连线.注意在实用的差,didInsertElement当使用主要(或可能"混凝土"是正确的术语)呈现视图,而didRenderSubjectPicker当视图的子部分呈现运行.
如果我想直接将事件发送到控制器,我只需将模板更改为:
{{trigger didRenderSubjectPicker target=controller}}
Run Code Online (Sandbox Code Playgroud)
并在我的控制器中执行此操作:
App.ReportPickerController = Ember.ArrayController.extend({
wireTypeahead: function(){
// I can access the rendered DOM elements here
}.on("didRenderSubjectPicker")
});
Run Code Online (Sandbox Code Playgroud)
完成!
需要注意的是,当视图子部分已经在屏幕上时(例如,如果重新渲染父视图),这可能会再次发生.但在我的情况下,无论如何都要再次运行typeahead初始化,如果需要的话,检测和编码很容易.在某些情况下可能需要这种行为.
我将此代码作为公共域名发布,不提供任何保证或承担任何责任.如果您想使用它,或者Ember人想要将它包含在基线中,请立即前进!(就个人而言,我认为这将是一个好主意,但这并不奇怪.)