Route @model argument value timing issue
We ran into a difference in behavior between @model and this.model that I'm not sure how to best describe but here is a twiddle that demonstrates the issue:
https://ember-twiddle.com/29dd9ad4820bdf987c92eb7acf1d7697?openFiles=routes.index%5C.js%2Ctemplates.components.person%5C.hbs&route=%2Fsome-other-route
It seems that the value of @model changes to undefined before all of the components in the template have been destroyed. This is different than the behavior seen when using this.model, where the value remains until after all of the components in the template have been destroyed.
For example, if we return a value from the model hook:
// app/routes/index.js
export default class IndexRoute extends Route {
model() {
return {
firstName: 'Little',
lastName: 'Sebastian'
};
}
}
And pass that value to a component in the route's template:
{{! app/templates/index.hbs }}
<Person @person={{@model}} />
And try to access the value in the component's willDestroy lifecycle hook:
// app/components/person.js
import Component from '@glimmer/component';
export default class PersonComponent extends Component {
willDestroy() {
console.log({ person: this.args.person });
// => { person: undefined }
}
}
Transitioning from the index route to another route causes the value of @model to become undefined by the time the willDestroy hook for the component runs.
This issue goes away of we instead pass the value to the component using this.model:
{{! app/templates/index.hbs }}
<Person @person={{this.model}} />
I tried making a failing test case in packages/@ember/-internals/glimmer/tests/integration/application/rendering-test.js, but ran in to an issue where this bug isn't present with @ember/component.
So, this may be an issue with @glimmer/components component-manager / maybe how @model and this.model are interpreted differently? unsure now.
for posterity, the passing test (using @ember/component):
async ['@feature(EMBER_ROUTING_MODEL_ARG) interior mutations on the model: @model exists as long as this.model (in willDestroy of component)'](
assert
) {
assert.expect(4);
this.router.map(function() {
this.route('foo');
});
this.add(
'route:index',
Route.extend({
model() {
return { firstName: 'Carol', lastName: 'Danvers' };
},
})
);
this.addComponent('person', {
ComponentClass: class PersonComponent extends Component {
tagName = '';
willDestroy() {
assert.ok(this.person, 'the @person arg still exists');
}
},
template: '{{@person.firstName}} {{@person.lastName}}',
});
this.addTemplate(
'index',
strip`
[@model: <Person @person={{@model}} />]
[this.model: <Person @person={{this.model}} />]
[model: <Person @person={{model}} />]
`
);
await this.visit('/');
this.assertInnerHTML(strip`
[@model: Carol Danvers]
[this.model: Carol Danvers]
[model: Carol Danvers]
`);
await this.visit('/foo');
}
An additional timing issue with @model. It seems that it is updated faster than the route template is torn down. So passing it into a component template and then following a link to another child route it will briefly hold the value of the new route. This hits us because we have a helper which calls model.hasMany(rel).ids() but the model for the second route doesn't have the same relationships and so this fails.
At our company we have run into this issue several times now. It's pain. Any updates from the core team on this?
Anyone able to confirm if this is still an issue in ember 3.25+?
This is probably a real issue (at one point anyway), but last time @rwjblue and I looked into it we were unable to reproduce/isolate the scenario where this happens: https://github.com/emberjs/ember.js/pull/19257
We just encountered this issue in a 3.25.3 app. The setup is almost the same as the OP (accessing value in willDestroy). One difference that was very surprising is that the value of @model is not undefined - it is somehow set to the model of the new route we are transitioning into 🤯 Switching to this.model also fixes the issue. I'll see if I can test with newer ember version or create a minimal reproduction app.
Update I was able to create a minimal reproduction case in a new 3.25 app, but not in a new 4.1 app - so looks like this was fixed in recent versions.
It's still an issue in Ember 6.x. I just ran into it when using the route .gjs feature.
I was able to reproduce the problem in a StackBlitz: https://stackblitz.com/edit/ember-cli-editor-output-exrpkxkg?file=app%2Ftemplates%2Fapplication.hbs
Things seem to go wrong when transitioning up in the route tree.
@Windvis what are repro instructions with that stackblitz?
@Windvis what are repro instructions with that stackblitz?
Instructions are in the application.hbs file, but in short:
- Open the dev console to see the logs
- Go to the Foo/Nested route
- Click one of the other links and check the logs in the console.
- Go back to Foo/Nested before clicking on a different link again.
Only the sibling route will log the correct value for @model.