7

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

Error: Object in path item_delet could not be found or was destroyed.

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

this.render('item_delete', { into: 'item_parent', outlet: 'item_delete' });

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

我正在使用 grunt-ember-templates 来编译 Handlebars 模板,因此发布 JSFiddle 有点困难。只是想知道是否有人可以“查看”此代码以标记路由或renderTemplate挂钩可能无法重新实例化/连接/等的任何明显原因。渲染的模板。我可以做一些“激活/停用”魔法来防止视图被破坏吗?(我意识到这与破坏视图对象的意图背道而驰,但我愿意听取所有选择。)

我有一个 Ember 路线图,如下所示:

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' });
        });
    });

});

我有这样定义的路线:

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...
    }

});

我正在为不同的路线/资源重用一些模板及其出口。例如,上面提到的“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>

在这个模板中,并不是所有的 outlet 都会得到一个渲染到它们的缓冲区,而在其他上下文中它们会。我这样做是为了避免 Handlebars 代码中不受欢迎的(令人困惑的)条件(以及通常的“isVisible”废话)。我对给定的模板感兴趣,根据需要“佩戴”它的视图;在某些情况下,可能有“create_button”和“cancel_button”,而在其他情况下,可能有“cancel_button”和“folder_actions”。

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

4

2 回答 2

3

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

在与 Tilde 的帮派进行了多次讨论后(感谢@peterwagenet,@codeofficer),我学会了“Ember 方式”来做我正在尝试的事情是使用 Ember 提供的生成的路由和控制器,并使用{{view}}帮助程序直接在 Handlebars 模板中渲染视图对象。

所以,在我上面的例子中,模板中应该没有App.ItemsIndexRoute.renderTemplate(), 和零的东西。{{outlet}}命名{{outlets}}用于渲染当前路由“外部”的路由,典型示例是模态,可能与插座正在渲染的父路由具有不同的模型。例如:

<div>
    {{view App.UsersListView}}
    {{outlet order_books_from_mars_modal}}
</div>

我所拥有的路线App.Router.map()非常正确,尽管我对生成的控制器有一些很大的误解。当你有一个App.FooRoute时,Ember 会设置一个App.FooController应该在应用程序代码的某处定义的。无论App.FooController你在路线的model钩子中给它什么,它都可以作为它的模型,比如:

App.FooRoute = Ember.Route.extend({

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

});

App.FooRoute并且为在is中呈现的所有视图提供了一个控制器App.FooController,它具有model前面在路由钩子中提供的模型。

并且路由器将寻找Ember.View与路由和控制器前缀匹配的对象,例如App.FooView. 所以,当有类似的事情时:

App.FooView = Ember.View.extend({

    template: 'some-foo-template'

});

... 匹配的路由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>

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

App.SubmissionButtonView = Ember.View.extend({

    template: 'the-killer-button'

});

...模板将类似于:

<script type="text/x-handlebars" data-template-name="the-killer-button">
    <a {{action someControllerMethod}}>Do Something</a>
</script>

预计someControllerMethod将在App.FooController. 如果在控制器上没有找到,则动作将冒泡到App.FooRoute'sevents对象。如果在链上的任何对象上都找不到该方法,则会记录一个错误,指示没有处理该操作someControllerMethod

因此,默认情况下App.FooController会提供给. 但是,您可以通过执行以下操作将不同的控制器绑定到它 - 例如,a -:App.SubmissionButtonViewApp.FooRouteApp.BarController

{{view App.SubmissionButtonView controllerBinding='App.BarController'}}

在这种情况下,someControllerMethod预计会在App.BarController或 上找到,App.FooRoute如前所述。

课程?

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

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

与往常一样,Ember 愚蠢的最佳资源是Ember 指南

于 2013-09-20T06:04:26.847 回答
1

看了下 ember 源码,错误来自 ember 的低级包 ember-metal - property_set.js

该代码表明它setPath期待一条路径并且正在获取没有路径的密钥。它在 和 切片上拆分.以获取父路径,这是丢失最后一个字符的地方。

尝试从该错误中查看堆栈跟踪,然后返回异常的来源。

发布具有堆栈跟踪的 jsbin 演示将有助于调试。

于 2013-06-25T13:20:55.243 回答