Class Route

public

The Route class is used to define individual routes. Refer to the routing guide for documentation.

Show:

Available since v1.0.0

transition
Transition

This hook is executed when the router enters the route. It is not executed when the model for the route changes.

key
String
The key to observe
target
Object
The target object to invoke
method
String|Function
The method to invoke
sync
Boolean
Whether the observer is sync or not
returns
Observable

Adds an observer on a property.

This is the core method used to register an observer for a property.

Once you call this method, any time the key's value is set, your observer will be notified. Note that the observers are triggered any time the value is set, regardless of whether it has actually changed. Your observer should be prepared to handle that.

There are two common invocation patterns for .addObserver():

  • Passing two arguments:
    • the name of the property to observe (as a string)
    • the function to invoke (an actual function)
  • Passing three arguments:
    • the name of the property to observe (as a string)
    • the target object (will be used to look up and invoke a function on)
    • the name of the function to invoke on the target object (as a string).
component.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import Component from '@ember/component';

export default Component.extend({
  init() {
    this._super(...arguments);

    // the following are equivalent:

    // using three arguments
    this.addObserver('foo', this, 'fooDidChange');

    // using two arguments
    this.addObserver('foo', (...args) => {
      this.fooDidChange(...args);
    });
  },

  fooDidChange() {
    // your custom logic code
  }
});

Observer Methods

Observer methods have the following signature:

component.js
1
2
3
4
5
6
7
8
9
10
11
12
import Component from '@ember/component';

export default Component.extend({
  init() {
    this._super(...arguments);
    this.addObserver('foo', this, 'fooDidChange');
  },

  fooDidChange(sender, key, value, rev) {
    // your code
  }
});

The sender is the object that changed. The key is the property that changes. The value property is currently reserved and unused. The rev is the last property revision of the object when it changed, which you can use to detect if the key value has really changed or not.

Usually you will not need the value or revision parameters at the end. In this case, it is common to write observer methods that take only a sender and key value as parameters or, if you aren't interested in any of these values, to write an observer that has no parameters at all.

Available since v1.0.0

resolvedModel
Object
the value returned from `model`, or its resolved value if it was a promise
transition
Transition
returns
Any | Promise<any>
if the value returned from this hook is a promise, the transition will pause until the transition resolves. Otherwise, non-promise return values are not utilized in any way.

This hook is called after this route's model has resolved. It follows identical async/promise semantics to beforeModel but is provided the route's resolved model in addition to the transition, and is therefore suited to performing logic that can only take place after the model has already resolved.

app/routes/posts.js
1
2
3
4
5
6
7
8
9
10
11
12
import Route from '@ember/routing/route';
import { service } from '@ember/service';

export default class PostsRoute extends Route {
  @service router;

  afterModel(posts, transition) {
    if (posts.get('length') === 1) {
      this.router.transitionTo('post.show', posts.get('firstObject'));
    }
  }
}

Refer to documentation for beforeModel for a description of transition-pausing semantics when a promise is returned from this hook.

Available since v1.0.0

transition
Transition
returns
Any | Promise<any>
if the value returned from this hook is a promise, the transition will pause until the transition resolves. Otherwise, non-promise return values are not utilized in any way.

This hook is the first of the route entry validation hooks called when an attempt is made to transition into a route or one of its children. It is called before model and afterModel, and is appropriate for cases when:

  1. A decision can be made to redirect elsewhere without needing to resolve the model first.
  2. Any async operations need to occur first before the model is attempted to be resolved.

This hook is provided the current transition attempt as a parameter, which can be used to .abort() the transition, save it for a later .retry(), or retrieve values set on it from a previous hook. You can also just call router.transitionTo to another route to implicitly abort the transition.

You can return a promise from this hook to pause the transition until the promise resolves (or rejects). This could be useful, for instance, for retrieving async code from the server that is required to enter a route.

Available since v3.10.0

returns
any

Allows you to produce custom metadata for the route. The return value of this method will be attached to its corresponding RouteInfoWithAttributes object.

Example

app/routes/posts/index.js
1
2
3
4
5
6
7
import Route from '@ember/routing/route';

export default class PostsIndexRoute extends Route {
  buildRouteInfoMetadata() {
    return { title: 'Posts Page' }
  }
}
app/routes/application.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import Route from '@ember/routing/route';
import { service } from '@ember/service';

export default class ApplicationRoute extends Route {
  @service router

  constructor() {
    super(...arguments);

    this.router.on('routeDidChange', transition => {
      document.title = transition.to.metadata.title;
      // would update document's title to "Posts Page"
    });
  }
}
keyName
String
returns
Object
The cached value of the computed property, if any

Returns the cached value of a computed property, if it exists. This allows you to inspect the value of a computed property without accidentally invoking it if it is intended to be generated lazily.

Available since v1.0.0

name
String
the name of the route or controller
returns
Controller | undefined

Returns the controller of the current route, or a parent (or any ancestor) route in a route hierarchy.

The controller instance must already have been created, either through entering the associated route or using generateController.

app/routes/post.js
1
2
3
4
5
6
7
8
9
import Route from '@ember/routing/route';

export default class PostRoute extends Route {
  setupController(controller, post) {
    super.setupController(controller, post);

    this.controllerFor('posts').set('currentPost', post);
  }
}

Available since v1.0.0

transition
Transition

This hook is executed when the router completely exits this route. It is not executed when the model for the route changes.

keyName
String
The name of the property to decrement
decrement
Number
The amount to decrement by. Defaults to 1
returns
Number
The new property value

Set the value of a property to the current value minus some amount.

1
2
player.decrementProperty('lives');
orc.decrementProperty('health', 5);
returns
EmberObject
receiver

Destroys an object by setting the isDestroyed flag and removing its metadata, which effectively destroys observers and bindings.

If you try to set a property on a destroyed object, an exception will be raised.

Note that destruction is scheduled for the end of the run loop and does not happen immediately. It will set an isDestroying flag immediately.

keyName
String
The property to retrieve
returns
Object
The property value or undefined.

Retrieves the value of a property from the object.

This method is usually similar to using object[keyName] or object.keyName, however it supports both computed properties and the unknownProperty handler.

Because get unifies the syntax for accessing all these kinds of properties, it can make many refactorings easier, such as replacing a simple property with a computed property, or vice versa.

Computed Properties

Computed properties are methods defined with the property modifier declared at the end, such as:

1
2
3
4
5
import { computed } from '@ember/object';

fullName: computed('firstName', 'lastName', function() {
  return this.get('firstName') + ' ' + this.get('lastName');
})

When you call get on a computed property, the function will be called and the return value will be returned instead of the function itself.

Unknown Properties

Likewise, if you try to call get on a property whose value is undefined, the unknownProperty() method will be called on the object. If this method returns any value other than undefined, it will be returned instead. This allows you to implement "virtual" properties that are not defined upfront.

list
String...|Array
of keys to get
returns
Object

To get the values of multiple properties at once, call getProperties with a list of strings or an array:

1
2
record.getProperties('firstName', 'lastName', 'zipCode');
// { firstName: 'John', lastName: 'Doe', zipCode: '10011' }

is equivalent to:

1
2
record.getProperties(['firstName', 'lastName', 'zipCode']);
// { firstName: 'John', lastName: 'Doe', zipCode: '10011' }
name
String
The name of the event
returns
Boolean
does the object have a subscription for event

Checks to see if object has any subscriptions for named event.

keyName
String
The name of the property to increment
increment
Number
The amount to increment by. Defaults to 1
returns
Number
The new property value

Set the value of a property to the current value plus some amount.

1
2
person.incrementProperty('age');
team.incrementProperty('score', 2);

An overridable method called when objects are instantiated. By default, does nothing unless it is overridden during class definition.

Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
import EmberObject from '@ember/object';

const Person = EmberObject.extend({
  init() {
    alert(`Name is ${this.get('name')}`);
  }
});

let steve = Person.create({
  name: 'Steve'
});

// alerts 'Name is Steve'.

NOTE: If you do override init for a framework class like Component from @ember/component, be sure to call this._super(...arguments) in your init declaration! If you don't, Ember may not have an opportunity to do important setup work, and you'll see strange behavior in your application.

Available since v1.2.0

name
String
the name of the route
models
...Object
the model(s) to be used while transitioning to the route.

Perform a synchronous transition into another route without attempting to resolve promises, update the URL, or abort any currently active asynchronous transitions (i.e. regular transitions caused by transitionTo or URL changes).

This method is handy for performing intermediate transitions on the way to a final destination route, and is called internally by the default implementations of the error and loading handlers.

Available since v1.0.0

params
Object
the parameters extracted from the URL
transition
Transition
returns
Any | Promise<any>
the model for this route. If a promise is returned, the transition will pause until the promise resolves, and the resolved value of the promise will be used as the model for this route.

A hook you can implement to convert the URL into the model for this route.

app/router.js
1
2
3
4
5
6
7
// ...

Router.map(function() {
  this.route('post', { path: '/posts/:post_id' });
});

export default Router;

Note that for routes with dynamic segments, this hook is not always executed. If the route is entered through a transition (e.g. when using the link-to Handlebars helper or the transitionTo method of routes), and a model context is already provided this hook is not called.

A model context does not include a primitive string or number, which does cause the model hook to be called.

Routes without dynamic segments will always execute the model hook.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// no dynamic segment, model hook always called
this.router.transitionTo('posts');

// model passed in, so model hook not called
thePost = store.findRecord('post', 1);
this.router.transitionTo('post', thePost);

// integer passed in, model hook is called
this.router.transitionTo('post', 1);

// model id passed in, model hook is called
// useful for forcing the hook to execute
thePost = store.findRecord('post', 1);
this.router.transitionTo('post', thePost.id);

This hook follows the asynchronous/promise semantics described in the documentation for beforeModel. In particular, if a promise returned from model fails, the error will be handled by the error hook on Route.

Note that the legacy behavior of automatically defining a model hook when a dynamic segment ending in _id is present is deprecated. You should explicitly define a model hook whenever any segments are present.

Example

app/routes/post.js
1
2
3
4
5
6
7
8
9
10
import Route from '@ember/routing/route';
import { service } from '@ember/service';

export default class PostRoute extends Route {
  @service store;

  model(params) {
    return this.store.findRecord('post', params.post_id);
  }
}

Available since v1.0.0

name
String
the name of the route
returns
Object
the model object

Returns the resolved model of a parent (or any ancestor) route in a route hierarchy. During a transition, all routes must resolve a model object, and if a route needs access to a parent route's model in order to resolve a model (or just reuse the model from a parent), it can call this.modelFor(theNameOfParentRoute) to retrieve it. If the ancestor route's model was a promise, its resolved result is returned.

Example

app/router.js
1
2
3
4
5
6
7
8
9
// ...

Router.map(function() {
  this.route('post', { path: '/posts/:post_id' }, function() {
    this.route('comments');
  });
});

export default Router;
app/routes/post/comments.js
1
2
3
4
5
6
7
8
9
import Route from '@ember/routing/route';

export default class PostCommentsRoute extends Route {
  model() {
    let post = this.modelFor('post');

    return post.comments;
  }
}
keyName
String
The property key to be notified about.
returns
Observable

Convenience method to call propertyWillChange and propertyDidChange in succession.

Notify the observer system that a property has just changed.

Sometimes you need to change a value directly or indirectly without actually calling get() or set() on it. In this case, you can use this method instead. Calling this method will notify all observers that the property has potentially changed value.

name
String
The name of the event
target
Object
The target of the subscription
method
Function|String
The function or the name of a function of the subscription
returns
this

Cancels subscription for given name, target, and method.

name
String
The name of the event
target
Object
The "this" binding for the callback
method
Function|String
A function or the name of a function to be called on `target`
returns
this

Subscribes to a named event with given function.

1
2
3
person.on('didLoad', function() {
  // fired once the person has loaded
});

An optional target can be passed in as the 2nd argument that will be set as the "this" for the callback. This is a good way to give your function access to the object triggering the event. When the target parameter is used the callback method becomes the third argument.

name
String
The name of the event
target
Object
The "this" binding for the callback
method
Function|String
A function or the name of a function to be called on `target`
returns
this

Subscribes a function to a named event and then cancels the subscription after the first time the event is triggered. It is good to use one when you only care about the first time an event has taken place.

This function takes an optional 2nd argument that will become the "this" value for the callback. When the target parameter is used the callback method becomes the third argument.

Available since v1.4.0

name
String
returns
Object
hash containing the parameters of the route `name`

Returns a hash containing the parameters of an ancestor route.

You may notice that this.paramsFor sometimes works when referring to a child route, but this behavior should not be relied upon as only ancestor routes are certain to be loaded in time.

Example

app/router.js
1
2
3
4
5
6
7
// ...

Router.map(function() {
  this.route('member', { path: ':name' }, function() {
    this.route('interest', { path: ':interest' });
  });
});
app/routes/member.js
1
2
3
4
5
6
7
import Route from '@ember/routing/route';

export default class MemberRoute extends Route {
  queryParams = {
    memberQp: { refreshModel: true }
  }
}
app/routes/member/interest.js
1
2
3
4
5
6
7
8
9
10
11
import Route from '@ember/routing/route';

export default class MemberInterestRoute extends Route {
  queryParams = {
    interestQp: { refreshModel: true }
  }

  model() {
    return this.paramsFor('member');
  }
}

If we visit /turing/maths?memberQp=member&interestQp=interest the model for the member.interest route is a hash with:

  • name: turing
  • memberQp: member

Available since v1.0.0

model
Object
the model for this route
transition
Transition
the transition object associated with the current transition

A hook you can implement to optionally redirect to another route.

Calling this.router.transitionTo from inside of the redirect hook will abort the current transition (into the route that has implemented redirect).

redirect and afterModel behave very similarly and are called almost at the same time, but they have an important distinction when calling this.router.transitionTo to a child route of the current route. From afterModel, this new transition invalidates the current transition, causing beforeModel, model, and afterModel hooks to be called again. But the same transition started from redirect does not invalidate the current transition. In other words, by the time the redirect hook has been called, both the resolved model and the attempted entry into this route are considered fully validated.

Available since v1.4.0

returns
Transition
the transition object associated with this attempted transition

Refresh the model on this route and any child routes, firing the beforeModel, model, and afterModel hooks in a similar fashion to how routes are entered when transitioning in from other route. The current route params (e.g. article_id) will be passed in to the respective model hooks, and if a different model is returned, setupController and associated route hooks will re-fire as well.

An example usage of this method is re-querying the server for the latest information using the same parameters as when the route was first entered.

Note that this will cause model hooks to fire even on routes that were provided a model object when the route was initially entered.

key
String
The key to observe
target
Object
The target object to invoke
method
String|Function
The method to invoke
sync
Boolean
Whether the observer is async or not
returns
Observable

Remove an observer you have previously registered on this object. Pass the same key, target, and method you passed to addObserver() and your target will no longer receive notifications.

Available since v1.7.0

controller
Controller
instance
isExiting
Boolean
transition
Object

A hook you can use to reset controller values either when the model changes or the route is exiting.

app/routes/articles.js
1
2
3
4
5
6
7
8
9
import Route from '@ember/routing/route';

export default class ArticlesRoute extends Route {
  resetController(controller, isExiting, transition) {
    if (isExiting && transition.targetName !== 'error') {
      controller.set('page', 1);
    }
  }
}

Available since v1.0.0

name
String
the name of the action to trigger
args
...*

Sends an action to the router, which will delegate it to the currently active route hierarchy per the bubbling rules explained under actions.

Example

app/router.js
1
2
3
4
5
6
7
// ...

Router.map(function() {
  this.route('index');
});

export default Router;
app/routes/application.js
1
2
3
4
5
6
7
8
9
import Route from '@ember/routing/route';
import { action } from '@ember/object';

export default class ApplicationRoute extends Route {
  @action
  track(arg) {
    console.log(arg, 'was clicked');
  }
}
app/routes/index.js
1
2
3
4
5
6
7
8
9
10
11
import Route from '@ember/routing/route';
import { action } from '@ember/object';

export default class IndexRoute extends Route {
  @action
  trackIfDebug(arg) {
    if (debug) {
      this.send('track', arg);
    }
  }
}

Available since v1.0.0

model
Object
the routes model
params
Array
an Array of parameter names for the current route (in the example, `['post_id']`.
returns
Object
the serialized parameters

A hook you can implement to convert the route's model into parameters for the URL.

app/router.js
1
2
3
4
5
// ...

Router.map(function() {
  this.route('post', { path: '/posts/:post_id' });
});
app/routes/post.js
1
2
3
4
5
6
7
8
9
10
11
12
13
import Route from '@ember/routing/route';

export default class PostRoute extends Route {
  model({ post_id }) {
    // the server returns `{ id: 12 }`
    return fetch(`/posts/${post_id}`;
  }

  serialize(model) {
    // this will make the URL `/posts/12`
    return { post_id: model.id };
  }
}

The default serialize method will insert the model's id into the route's dynamic segment (in this case, :post_id) if the segment contains '_id'. If the route has multiple dynamic segments or does not contain '_id', serialize will return getProperties(model, params)

This method is called when transitionTo is called with a context in order to populate the URL.

keyName
String
The property to set
value
Object
The value to set or `null`.
returns
Object
The passed value

Sets the provided key or path to the value.

1
record.set("key", value);

This method is generally very similar to calling object["key"] = value or object.key = value, except that it provides support for computed properties, the setUnknownProperty() method and property observers.

Computed Properties

If you try to set a value on a key that has a computed property handler defined (see the get() method for an example), then set() will call that method, passing both the value and key instead of simply changing the value itself. This is useful for those times when you need to implement a property that is composed of one or more member properties.

Unknown Properties

If you try to set a value on a key that is undefined in the target object, then the setUnknownProperty() handler will be called instead. This gives you an opportunity to implement complex "virtual" properties that are not predefined on the object. If setUnknownProperty() returns undefined, then set() will simply set the value on the object.

Property Observers

In addition to changing the property, set() will also register a property change with the object. Unless you have placed this call inside of a beginPropertyChanges() and endPropertyChanges(), any "local" observers (i.e. observer methods declared on the same object), will be called immediately. Any "remote" observers (i.e. observer methods declared on another object) will be placed in a queue and called at a later time in a coalesced manner.

hash
Object
the hash of keys and values to set
returns
Object
The passed in hash

Sets a list of properties at once. These properties are set inside a single beginPropertyChanges and endPropertyChanges batch, so observers will be buffered.

1
record.setProperties({ firstName: 'Charles', lastName: 'Jolley' });

Available since v1.0.0

controller
Controller
instance
model
Object
transition
Transition

A hook you can use to setup the controller for the current route.

This method is called with the controller for the current route and the model supplied by the model hook.

By default, the setupController hook sets the model property of the controller to the specified model when it is not undefined.

If you implement the setupController hook in your Route, it will prevent this default behavior. If you want to preserve that behavior when implementing your setupController function, make sure to call super:

app/routes/photos.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import Route from '@ember/routing/route';
import { service } from '@ember/service';

export default class PhotosRoute extends Route {
  @service store;

  model() {
    return this.store.findAll('photo');
  }

  setupController(controller, model) {
    super.setupController(controller, model);

    this.controllerFor('application').set('showingPhotos', true);
  }
}

The provided controller will be one resolved based on the name of this route.

If no explicit controller is defined, Ember will automatically create one.

As an example, consider the router:

app/router.js
1
2
3
4
5
6
7
// ...

Router.map(function() {
  this.route('post', { path: '/posts/:post_id' });
});

export default Router;

If you have defined a file for the post controller, the framework will use it. If it is not defined, a basic Controller instance would be used.

returns
String
string representation

Returns a string representation which attempts to provide more information than Javascript's toString typically does, in a generic way for all Ember objects.

1
2
3
4
5
import EmberObject from '@ember/object';

const Person = EmberObject.extend();
person = Person.create();
person.toString(); //=> "<Person:ember1024>"

If the object's class is not defined on an Ember namespace, it will indicate it is a subclass of the registered superclass:

1
2
3
const Student = Person.extend();
let student = Student.create();
student.toString(); //=> "<(subclass of Person):ember1025>"

If the method toStringExtension is defined, its return value will be included in the output.

1
2
3
4
5
6
7
const Teacher = Person.extend({
  toStringExtension() {
    return this.get('fullName');
  }
});
teacher = Teacher.create();
teacher.toString(); //=> "<Teacher:ember1026:Tom Dale>"
keyName
String
The name of the property to toggle
returns
Boolean
The new property value

Set the value of a boolean property to the opposite of its current value.

1
starship.toggleProperty('warpDriveEngaged');
name
String
The name of the event
args
Object...
Optional arguments to pass on

Triggers a named event for the object. Any additional arguments will be passed as parameters to the functions that are subscribed to the event.

1
2
3
4
5
6
7
person.on('didEat', function(food) {
  console.log('person ate some ' + food);
});

person.trigger('didEat', 'broccoli');

// outputs: person ate some broccoli

Override to implement teardown.