路径转换破坏渲染的视图对象; "错误:路径[blah]中的对象无法找到或被破坏."

mys*_*une 7 handlebars.js ember.js ember-router

当从一个Ember路由转换到另一个Ember路由时,我收到以下错误:

Error: Object in path item_delet could not be found or was destroyed.
Run Code Online (Sandbox Code Playgroud)

在路线的renderTemplate钩子里,我做了很多这样的事情:

this.render('item_delete', { into: 'item_parent', outlet: 'item_delete' });
Run Code Online (Sandbox Code Playgroud)

...并有一个合理的父/子模板树.但是,当一个模板,比如说"item_delete",被渲染成"routeA",然后我点击进入"routeB",然后回到"routeA",我得到了错误.据我所知,当路由器退出"routeA"以防止内存泄漏时,视图对象会被破坏.我不确定为什么重新进入路由不会重新创建/实例化/连接视图.作为旁注,当出现错误时,任何先前呈现的获取此错误消息的视图的路径名称始终缩短一个字符,请注意"item_delet"而不是"item_delete".

我正在使用grunt-ember-templates来编译Handlebars模板,因此发布JSFiddle有点困难.只是想知道是否有人可以"看到"这段代码来标记路由或renderTemplate钩子可能无法重新实例化/连接/等的任何明显原因.渲染的模板.是否有一些"激活/停用"魔法可以阻止视图被破坏?(我意识到面对摧毁视图对象背后的意图,但我愿意听到所有选项.)

我有一个Ember Route地图,看起来像:

App.Router.map(function () {

    this.route('index', { path: '/projects' });
    this.resource('items', { path: '/projects/folders' }, function() {
        this.resource('item', { path: '/:item_id' }, function() {
            this.route('file_uploads', { path: '/file_upload' });
        });
    });

});
Run Code Online (Sandbox Code Playgroud)

我有像这样定义的路线:

App.IndexRoute = Ember.Route.extend({
    redirect: function() {
        this.transitionTo('items');
    }
});

App.ItemsIndexRoute = Ember.Route.extend({

    model: function() {
        // Setting up the model
    }
    , setupController: function(controller, model) {
        // Setting up some controllers
    }
    , renderTemplate: function() {
        this.render('index', {
            into: 'application'
            , outlet: 'application'
            , controller: this.controllerFor('items')
        });

        this.render('navbar', {
            into: 'application'
            , outlet: 'navbar'
            , controller: this.controllerFor('currentUser')
        });

        this.render('items', {
            into: 'index'
            , outlet: 'index'
            , controller: this.controllerFor('items')
        });

        this.render('items_toolbar', {
            into: 'index'
            , outlet: 'items_toolbar'
            , controller: this.controllerFor('items')
        });

        this.render('item_rename', {
            into: 'items_toolbar'
            , outlet: 'item_rename'
            , controller: this.controllerFor('items')
        });

        this.render('item_delete', {
            into: 'items_toolbar'
            , outlet: 'item_delete'
            , controller: this.controllerFor('items')
        });

        // ... some more of these...

    }

});

App.ItemRoute = Ember.Route.extend({

    model: function (params) {
        // Building the model for the route
    }
    , setupController: function(controller, model) {
        // Setting up some controllers
    }
    , renderTemplate: function() {
        this.render('index', {
            into: 'application'
            , outlet: 'application'
            , controller: this.controllerFor('items')
        });

        this.render('navbar', {
            outlet: 'navbar'
            , into: 'application'
            , controller: this.controllerFor('application')
        });

        this.render('items', {
            into: 'index'
            , outlet: 'index'
            , controller: this.controllerFor('items')
        });

        this.render('items_toolbar', {
            into: 'index'
            , outlet: 'items_toolbar'
            , controller: this.controllerFor('items')
        });

        this.render('item_rename', {
            into: 'items_toolbar'
            , outlet: 'item_rename'
            , controller: this.controllerFor('items')
        });

        this.render('item_delete', {
            into: 'items_toolbar'
            , outlet: 'item_delete'
            , controller: this.controllerFor('items')
        });

        // ... some more of these...

    }

});

App.ItemFileUploadsRoute = Ember.Route.extend({

    model: function() {
        // Setting up the model
    }

    , setupController: function(controller, model) {
        // Setting up some controllers
    }

    , renderTemplate: function() {

        this.render('file_uploads', {
            into: 'application'
            , outlet: 'application'
            , controller: this.controllerFor('fileUploads')
        });

        this.render('navbar', {
            into: 'application'
            , outlet: 'navbar'
            , controller: this.controllerFor('application')
        });

        this.render('items_toolbar', {
            into: 'file_uploads'
            , outlet: 'items_toolbar'
            , controller: this.controllerFor('fileUploads')
        });

        this.render('item_rename', {
            into: 'items_toolbar'
            , outlet: 'item_rename'
            , controller: this.controllerFor('items')
        });

        this.render('item_delete', {
            into: 'items_toolbar'
            , outlet: 'item_delete'
            , controller: this.controllerFor('items')
        });

        // ... some more of these...
    }

});
Run Code Online (Sandbox Code Playgroud)

我正在重复使用一些模板及其出口来获取不同的路径/资源.例如,上面提到的"items_toolbar"在Handlebars模板中,如下所示:

<div class="row toolbar">
    <div class="col col-lg-6 text-right">
        {{outlet submission_options_button}}
        {{outlet submission_button}}
        {{outlet create_button}}
        {{outlet confirm_button}}
        {{outlet cancel_button}}
        {{outlet folder_actions}}
        {{outlet item_rename}}
        {{outlet item_delete}}
    </div>
</div>
Run Code Online (Sandbox Code Playgroud)

在这个模板中,并不是所有的出口都会得到一个缓冲区,而在其他情况下它们会.我这样做是为了避免Handlebars代码中的不良(令人困惑)条件(以及通常的"isVisible"无意义).我对一个给定的模板感兴趣,根据需要"佩戴"它的观点; 在某些情况下,可能存在"create_button"和"cancel_button",在其他情况下可能存在"cancel_button"和"folder_actions".

是否有任何可靠的方法来确保在重新进入路径时,先前在其中呈现然后销毁的任何对象可以重新连接,重新初始化和/或重新呈现?

mys*_*une 3

对此感兴趣的朋友继续关注。事实证明,在我的方法中(过度)使用{{outlet}}- 更不用说在路由器中将模板渲染到其中 - 是一种反模式。

经过与 Tilde 的团队进行多次讨论(感谢 @peterwagenet、@codeofficer),我学会了“Ember Way”来完成我想要做的事情,即使用 Ember 提供的生成的路由和控制器,并使用帮助程序{{view}}来直接渲染 Handlebars 模板中的视图对象。

因此,在我上面的例子中,模板中应该几乎没有App.ItemsIndexRoute.renderTemplate(), 和零的内容。{{outlet}}命名{{outlets}}意味着渲染当前路线“外部”的路线,典型的例子是模态,它可能具有与渲染出口的父路线不同的模型。例如:

<div>
    {{view App.UsersListView}}
    {{outlet order_books_from_mars_modal}}
</div>
Run Code Online (Sandbox Code Playgroud)

我的路线App.Router.map()几乎是正确的,尽管我对生成的控制器有一些很大的误解。当您有一个 时App.FooRoute,Ember 会设置一个App.FooController应在应用程序代码中的某个位置定义的 。它App.FooController可以将您在路线model挂钩中提供的任何内容作为其模型,例如:

App.FooRoute = Ember.Route.extend({

    model: function() {
        return $.get('/some/resource', function() { ... });
    }

});
Run Code Online (Sandbox Code Playgroud)

App.FooRoute为is中渲染的所有视图提供一个控制器App.FooController,该控制器具有之前在路由model钩子中提供的模型。

路由器将寻找与Ember.View路由和控制器前缀匹配的对象,例如App.FooView。所以,当出现这样的情况时:

App.FooView = Ember.View.extend({

    template: 'some-foo-template'

});
Run Code Online (Sandbox Code Playgroud)

... 匹配的路由App.FooController将渲染App.FooView, 以及视图template属性中指示的模板。就像是:

<script type="text/x-handlebars" data-template-name="some-foo-template">
    <div class="row toolbar">
        <div class="col col-lg-6 text-right">
            {{view App.SubmissionButtonView}}
        </div>
    </div>
</script>
Run Code Online (Sandbox Code Playgroud)

在此视图中, anApp.SubmissionButtonView正在使用{{view}}助手进行渲染。所以,需要有一个对应的:

App.SubmissionButtonView = Ember.View.extend({

    template: 'the-killer-button'

});
Run Code Online (Sandbox Code Playgroud)

...模板将类似于:

<script type="text/x-handlebars" data-template-name="the-killer-button">
    <a {{action someControllerMethod}}>Do Something</a>
</script>
Run Code Online (Sandbox Code Playgroud)

预计将someControllerMethod在 上找到App.FooController。如果在控制器上找不到它,则该操作将冒泡到 的App.FooRoute对象events。如果在链上的任何对象上都找不到该方法,则会记录一条错误,表明没有任何内容处理该操作someControllerMethod

因此,默认情况App.FooController下将提供给 ,App.SubmissionButtonView因为它是在App.FooRoute. 但是,您可以通过执行以下操作将不同的控制器绑定到它(例如,一个)App.BarController

{{view App.SubmissionButtonView controllerBinding='App.BarController'}}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,预计可以在或前面提到的上someControllerMethod找到。App.BarControllerApp.FooRoute

课程?

最后,对我来说,真正的教训是如何正确建立路由层次结构,利用 Ember 提供的生成控制器,并使用 helper而不是我最初尝试的{{view}}命名s 来渲染视图。{{outlet}}Ember 期望命名路由有相应的控制器和相应的视图。一旦我明白了这一点,一切就开始对我来说更好地结合在一起。

就我而言,现在因为嵌套资源都适当地处理渲染它们的子视图——当用户导航“远离”当前路线时销毁它们,当用户导航到当前资源/路线“内部”时保留它们——我发布的原始错误不再发生。

与往常一样,让 Ember 自己变得愚蠢的最佳资源是Ember 指南