Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ember.js/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ember.js-使用Handlebar辅助对象检测子视图是否已渲染_Ember.js - Fatal编程技术网

Ember.js-使用Handlebar辅助对象检测子视图是否已渲染

Ember.js-使用Handlebar辅助对象检测子视图是否已渲染,ember.js,Ember.js,有许多问题以这样或那样的方式提出:“在呈现视图的某个部分之后,我该如何做一些事情?”(仅举几个例子)。答案通常是: 使用didInsertElement在最初渲染视图时运行代码 如果需要访问创建的DOM元素,请使用Ember.run.next(…)在刷新视图更改后运行代码 加载所需数据后,使用isLoaded上的观察者或类似属性执行操作 令人恼火的是,这会导致一些看起来非常笨拙的事情,比如: didInsertElement: function(){ content.on('didLoa

有许多问题以这样或那样的方式提出:“在呈现视图的某个部分之后,我该如何做一些事情?”(仅举几个例子)。答案通常是:

  • 使用
    didInsertElement
    在最初渲染视图时运行代码
  • 如果需要访问创建的DOM元素,请使用
    Ember.run.next(…)
    在刷新视图更改后运行代码
  • 加载所需数据后,使用
    isLoaded
    上的观察者或类似属性执行操作
  • 令人恼火的是,这会导致一些看起来非常笨拙的事情,比如:

    didInsertElement: function(){
        content.on('didLoad', function(){
            Ember.run.next(function(){
                // now finally do my stuff
            });
        });
    }
    
    当您使用ember数据时,这甚至不一定有效,因为
    isLoaded
    可能已经是真的(如果之前已经加载了记录,并且没有再次从服务器请求记录)。因此,获得正确的排序是很困难的

    除此之外,您可能已经在视图模板中看到isLoaded,如下所示:

    {{#if content.isLoaded}}
        <input type="text" id="myTypeahead" data-provide="typeahead">
    {{else}}
        <div>Loading data...</div>
    {{/if}}
    
    其用法如下:

    {{#if content.isLoaded}}
        {{fire typeaheadHostDidRender}}
        <input type="text" id="myTypeahead" data-provide="typeahead">
    {{else}}
        <div>Loading data...</div>
    {{/if}}
    
    现在,我所缺少的只是弄清楚如何传入预期目标(例如,控制器或视图——上面的代码试图猜测)。或者,弄清楚是否有一些意想不到的行为打破了整个概念

    任何其他输入?

    已更新 更新为Ember 1.0最终版本,我目前正在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);
        });
    });
    
    我将名称从
    {{fire}}
    更改为
    {{trigger}}
    ,以更紧密地匹配Ember.Evented/jQuery约定。此更新的代码基于内置的Ember
    {{action}
    helper,应该能够接受模板中的任何
    target=“…”
    参数,就像
    {{action}
    一样。与
    {{action}}
    不同的是(除了在呈现模板节时自动触发外):

  • 默认情况下,将事件发送到视图。默认情况下,发送到路由或控制器没有多大意义,因为这可能主要用于以视图为中心的操作(尽管我经常使用它向控制器发送事件)
  • 使用Ember.Evented样式的事件,因此要将事件发送到任意非视图对象(包括控制器),对象必须扩展Ember.Evented,并且必须注册侦听器。(需要明确的是,它不会调用
    actions:{…}
    hash!中的内容)
  • 请注意,如果将事件发送到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">&times;</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>
    
    然后在my view类中实现了
    didRenderSubjectPicker

    App.ReportPickerView = Ember.View.extend({
        templateName: 'reportPicker',
    
        didInsertElement: function () {
            this.get('controller').viewDidLoad(this);
        }
        ,
        didRenderSubjectPicker: function () {
            this.get('controller').wireTypeahead();
            $('#subjectPicker').focus();
        }
    
    });
    
    完成了!现在,当(并且仅当)模板的子部分最终呈现时,typeahead才会连接。请注意实用程序的不同之处,
    didInsertElement
    在渲染主视图(或者“混凝土”是合适的术语)时使用,而在渲染视图的子部分时运行
    didRenderSubjectPicker

    如果我想直接将事件发送到控制器,我只需将模板更改为:

    {{trigger didRenderSubjectPicker target=controller}}
    
    并在我的控制器中执行此操作:

    App.ReportPickerController = Ember.ArrayController.extend({
        wireTypeahead: function(){
            // I can access the rendered DOM elements here
        }.on("didRenderSubjectPicker")
    });
    
    完成了

    需要注意的一点是,当视图子部分已在屏幕上时(例如,如果重新渲染父视图),可能会再次发生这种情况。但在我的例子中,再次运行typeahead初始化是可以的,如果需要的话,它将非常容易检测和编码。在某些情况下,这种行为可能是需要的


    我将此代码作为公共域发布,不提供任何担保或承担任何责任。如果你想使用这个,或者余烬的人想把它包括在基线中,那就直接开始吧!(我个人认为这是一个好主意,但这并不奇怪。)

    作为旁注,我意识到另一种方法是为每个子视图显式定义视图对象和模板……这样您就可以在每个子视图上显式查看
    diInsertElement
    事件。但这似乎真的太过分了,因为有些东西只需要
    {{{if isLoaded}}
    就可以工作。但是,如果“子视图”更复杂,这可能是一个更好的选择。这个解决方案是相关的:感谢您编写这个帮助程序。我发现并使用它来寻找一个解决方案,当底层内容发生变化时重新调整我的HTML,它工作得很好。也就是说,我最近找到了一个更合适的解决方案,解决了我得到的未对齐的“闪烁”:didUpdateContent:function(){Ember.run.scheduleOnce('afterRender',this,'alignHtmlMethod');}.observes('controller.content'),正如
    Ember.run.next
    的文档中所建议的那样:我想在这里发布这篇文章,供未来的开发人员参考。事实上,由于
    {trigger}}
    在呈现HTML后按设计运行,使用它来移动呈现的HTML会产生“闪烁”效果。可能不容易更改,因为我构建它的用例要求DOM就位(例如,为了将jQuery插件附加到元素)。不确定
    didUpdateContent
    ,但我猜它不会明确告诉您模板的哪一部分已更新,而不会将其分解为子视图/子模板,这正是
    {{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}}
    
    App.ReportPickerView = Ember.View.extend({
        templateName: 'reportPicker',
    
        didInsertElement: function () {
            this.get('controller').viewDidLoad(this);
        }
        ,
        didRenderSubjectPicker: function () {
            this.get('controller').wireTypeahead();
            $('#subjectPicker').focus();
        }
    
    });
    
    {{trigger didRenderSubjectPicker target=controller}}
    
    App.ReportPickerController = Ember.ArrayController.extend({
        wireTypeahead: function(){
            // I can access the rendered DOM elements here
        }.on("didRenderSubjectPicker")
    });