Route transitions destroy the displayed view object; "Error: the object on the [blah] path could not be found or destroyed."

When switching from one Ember route to another, I get the following error:

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

In renderTemplate route renderTemplate I do a lot of this:

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

... and have a rational tree of parent / child templates. However, when the pattern, say, "item_delete" is displayed in "routeA", then I click on "routeB" and then return to "routeA", I get an error. I understand that a view object is destroyed when a router exits "routeA" in order to prevent memory leaks. I'm not sure why re-entering the route does not recreate / create / join the view. As a side note, when presenting an error, any of the previously rendered views that receive this error message always has a path name abbreviated by one character, instead of “item_delete”, “item_delet” should be noted.

I use grunt-ember templates to compile Handlebars templates, so posting a JSFiddle is a bit difficult. It's just interesting if anyone can “aim” at this code to point out any obvious reasons why renderTemplate routes or hooks might not be updated / connected / etc. displayed templates. Is there some kind of “activate / deactivate” magic that I can do to prevent the destruction of ideas? (I understand that flies, in the face of intentions, still destroy species, but I’m ready to hear all the options.)

I have an Ember Route map that looks like this:

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

I have routes defined as follows:

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

I reuse some patterns and their outputs for different routes / resources. For example, the above “items_toolbar” is in the Handlebars template as follows:

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

In this template, not all outputs will receive a buffer in them, and in other contexts they will. I do this to avoid unwanted (confusing) conventions in the Handlebars code (and the usual "isVisible" bullshit). I am interested in the fact that this template "wore" their views as necessary; in some cases there can be "create_button" and "cancel_button", and in other cases there can be "cancel_button" and "folder_actions".

Is there any reliable way to make sure that when re-entering a route, any objects that were previously rendered in it, then destroyed, can be reconnected, reinitialized and / or re-mapped?

+7
source share
2 answers

Following this for those who are interested. It turns out that using the {{outlet}} method in my approach - not to mention rendering the templates in them inside the router - is an anti-template.

After a series of discussions with the gang in Tilde (thanks @peterwagenet, @codeofficer), I found out that "Ember Way" does what I'm trying to use, it uses the generated routes and controllers that Ember provides and uses {{view}} to visualize view objects in Handlebars templates directly.

So, in my case above, there should be nothing in the template except App.ItemsIndexRoute.renderTemplate() , and zero {{outlet}} . Named {{outlets}} are intended to render routes that are "external" to the current route, the canonical example is modals that may have a different model than the parent route on which the rendering is being performed. For example:

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

The routes in App.Router.map() that I had are very correct, although with a few big misunderstandings on my part regarding the generated controllers. When you have App.FooRoute , Ember installs App.FooController , which must be defined somewhere in the application code. That App.FooController can have as its model everything that you give it in the model route, for example:

 App.FooRoute = Ember.Route.extend({ model: function() { return $.get('/some/resource', function() { ... }); } }); 

And the controller that is set for all the views displayed in App.FooRoute is App.FooController , which has a model that was previously provided in the model route.

And the router will look for Ember.View objects that match the route and controller prefix, for example. App.FooView . So, when there is something like:

 App.FooView = Ember.View.extend({ template: 'some-foo-template' }); 

... the route that matches App.FooController will display App.FooView and the template specified in the template . Something like:

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

And in this view, the App.SubmissionButtonView is created using the {{view}} helper. Thus, there should be a corresponding:

 App.SubmissionButtonView = Ember.View.extend({ template: 'the-killer-button' }); 

... where the template will look something like this:

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

someControllerMethod is someControllerMethod be found on App.FooController . If it is not found on the controller, the action will bubble up to the App.FooRoute events object. If the method is not found on any object in the chain, an error will be logged, indicating that nothing was processed by the action of someControllerMethod .

And therefore, App.FooController will be what is provided by App.SubmissionButtonView by default, as it is rendering inside App.FooRoute . However, you can associate another controller with it - for example, App.BarController - by doing:

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

In this case, it is expected that someControllerMethod will be found on App.BarController or on App.FooRoute , as mentioned earlier.

Lesson?

At the end of all this, the lesson for me was really setting up the hierarchy of routes using the created Ember controllers and rendering the views using the {{view}} and NOT helper with the name {{outlet}} as I first tried. Ember expects the named route to have an appropriate controller and corresponding representation. As soon as I realized this, everything became better to support me.

In my case, now, because the nested resources handle the rendering of their child views accordingly - destroying them when the user moves away from the current route, saving them when the user moves around the "inside" of the current resource / route - The original error about which I wrote, no longer happens.

As always, the best resource for Embering yourself stupid is Ember Guides .

+3
source

I looked at the source of ember, the error comes from the low-level ember package, ember-metal is property_set.js .

The code assumes that setPath expects a path and receives a key without a path. It is broken into . and slices to get the parent path in which it will lose the last character.

Try to find the stack trace from this error and return twice to where the exception occurs.

Posting a jsbin demo with a stack trace will help in debugging.

+1
source

All Articles