Model in route template

What is the difference between using @model and this.model in a route template?

I encountered a bug in our codebase today and noticed two route templates were making use of the same component hierarchy so traced the issue back to the template that was passing the model to the components as @model i.e. @entries=@model.entries.

The issue was the Assertion Failed:

Attempting to update a value after using it in a computation can
cause logical errors, infinite revalidation bugs, and performance
issues, and is not supported.

For a reason I’m not understanding, it seems that converting the usage to this.model in the route template fixed the issue for me.

Ember Version: 3.20.6 LTS

You’re very likely running into a side effect of the differences between @model and this.model explored in some detail in this GitHub issue. As described there, the @model value in a route template is exactly the model as it was resolved by the model (and potentially afterModel hook in the route, while this.model is the property on the controller, which by default is the same object set on the controller in the default setupController implementation.

This can have a number of somewhat-surprising side effects. For example, when navigating out of a given route, @model will become undefined when the route is torn down, until the model for the new route resolves. However, this.model will not, since it is a property on the long-lived (normally application-life-long-lived!) controller singleton for the route. While this difference doesn’t normally show up in templates, it can show up if you somehow trigger a promise which references @model or this.model, or try to refer to it via the destruction hooks in a modifier in a template.

In your specific case, it’s not obvious exactly what the issue is, but I’ve sometimes seen this kind of thing when someone is passing a value from @model into something which mutates it. I would hypothesize that the way @model is sort of “frozen” to the result of the model hook vs. the way that this.model is just an object property on the controller ends up triggering the assertion when mutating it in some way downstream, including by directly setting properties on it.

Your best bet here to start with would be to identify exactly what is triggering the assertion. My normal workflow here is to have dev tools up, let it hit the assertion, and follow the link in the console back to the assertion. Then set a breakpoint on the assertion, and walk back up the call stack. That will be a bit thorny, as you’ll be fairly deep inside Glimmer’s internals for how it sets values like this, but you should ultimately be able to see what property is being set, on what object, that triggers this specific instance of the assertion.

Thanks for the thorough response, so it seems like from reading the github issue and associated RFC surrounding the introduction of @model that it would be best practice to use @model in all route templates, is that correct (at least for cases where the model is not being modified in the setupController method)?

It’s sounding like our usage of this.model might be masking some incorrect usage patterns and isn’t the real solution for avoiding the assertion failure I mentioned.

I think that’s what I would take away, yep. If you think of a controller/template pair like a really funky component, that’s the right mental model (not least because the template is a really funky component which receives @model as an argument and has a controller as its backing class!). In that mental model, setupController feels weird: it’s like reaching into a component’s baking class and mucking with its internal state rather than sticking to arguments and injections. I’m out codebase were steadily moving away from it, because it’s a consistent source of all sorts of bugs, including some like this.

Yeah I think the setupController was the piece that was missing in my understanding. In fact we had a route which defined its own setupController and I was wondering why this.model wasn’t working in the template so now it’s all clicking. Thanks for this.

Actually I also thought so but got confused by a recent Ember Template Lint version adding a rule that forbids @model in route templates: ember-template-lint/ at master · ember-template-lint/ember-template-lint · GitHub

Some guidance from core team on that topic would be great.

The existence of a lint rule is different from its being recommended! In this case, there’s no guidance against using {{@model}} in route templates, just a tool if that’s what your team finds is a better fit vs. trying to explain it to your developers. The “right” move there will depend on the size of the team, their seniority/juniority, etc.