Routes, Templates & Components
Routing & the model() Hook
// app/router.js
Router.map(function () {
this.route('posts');
this.route('post', { path: '/posts/:post_id' });
});
// app/routes/post.js
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
export default class PostRoute extends Route {
@service store;
// Ember waits for this Promise to resolve BEFORE rendering the template —
// the template can always assume model data is already loaded
async model(params) {
return this.store.findRecord('post', params.post_id);
}
}{{! app/templates/post.hbs }}
<h1>{{@model.title}}</h1>
<p>{{@model.body}}</p>
{{! ember generate route post — scaffolds the route file, updates
router.js, and creates this starter template automatically }}Octane Components (@tracked)
// app/components/counter.js
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
export default class CounterComponent extends Component {
@tracked count = 0; // reactive — templates referencing it re-render on change
@action
increment() {
this.count++;
}
}{{! app/components/counter.hbs }}
<p>Count: {{this.count}}</p>
<button {{on "click" this.increment}}>+1</button>
{{! Usage — @title is a named argument, accessible as this.args.title }}
<Counter @title="My Counter" />Keep your own version of these notes — editable, searchable, and organised by your stack.
Start free