The CanReuse and OnReuse hooks
Last but not least, we can reuse the same instance of a component while browsing from one component to another component of the same type. This way, we can skip the process of destroying and instantiating a new component, saving resources on the go.
This requires us to ensure that the information contained in the parameters and stuff is properly handled to refresh the component UI or logic if required in the new incarnation of the same component.
The CanReuse
hook is responsible for all this, and it tells the Router
whether the component should be freshly instantiated or whether we should reuse the component in the future calls of the same route. The CanReuse
interface method should return a Boolean value or a Promise
resolving to a Boolean value (just like the CanActivate
or CanDeactivate
hooks do), which informs the Router
if it should reuse this component in the next call. If the CanReuse
implementation throws an error or is rejected from within the Promise
, the navigation will be cancelled.
On the other hand, if the CanReuse
interface returns or resolves to true, the OnReuse
hook will be executed instead of the OnActivate
hook should the latter exist already in the component. Therefore, use only one of these two whenever you implement this functionality.
Let’s see all these in an actual example. When we schedule a task in the task list table and proceed to its timer, we can jump at any time to the generic timer accessible from the top nav bar, thereby loading another timer that is not bound to any task whatsoever. By doing so, we are actually jumping from one instance of the TimerWidgetComponent
component to another TimerWidgetComponent
component and the Angular router will destroy and instantiate the same component again. We can save the Router
from doing so by configuring the component to be reused. Open the TimerWidgetComponent
module and import the interfaces we will need for this, along with the symbols we were importing already from the Router
library:
app/timer/timer-widget.component.ts
import { Component, OnInit } from '@angular/core';
import { SettingsService, TaskService } from '../shared/shared';
import { RouteParams, CanReuse, OnReuse } from '@angular/router-deprecated';
Now, implement the CanReuse
and OnReuse
interfaces in the class by adding them to the implements declaration and then proceed to attach the following required interface methods to the class body:
routerCanReuse(): boolean { return true; } routerOnReuse(next: ComponentInstruction): void { // No implementation yet }
Now go to the tasks table, schedule any task, and go to its timer. Click on the Timer link in the top nav bar. You will see how the URL changes in the browser but nothing happens. We are reusing the same component as it is. While this saves memory resources, we need a fresh timer when performing this action. So, let’s update the OnReuse
method accordingly, resetting the taskName
value and the Pomodoro itself:
routerOnReuse(): void { this.taskName = null; this.isPaused = false; this.resetPomodoro(); }
Reproduce now the same navigation journey and see what happens. Voila! New behavior but same old component.
Advanced tips and tricks
Although we have discussed all that you need to start building complex applications with routing functionalities, there is still a big collection of advanced techniques you can use to take our application to the next level. In the upcoming sections, we will highlight just a few.
Redirecting to other routes
Besides the route definition types we have seen already, there is another RouteDefinition
type named Redirect
that is not bound to any named Route
or component
, but will rather redirect to another existing Route
.
So far, we were serving the task list table from the root path, but what if we want to deliver this table from a path named /tasks
while ensuring that all the links pointing to the root are properly handled? Let’s create a redirect route then. We will update the top root router configuration with a new path for the existing home path and a redirect path to it from the new home URL. The code is as follows:
app/app.component.ts
... @RouteConfig([{ path: '', name: 'Home', redirectTo: ['TasksComponent'] }, { path: 'tasks', name: 'TasksComponent', component: TasksComponent, useAsDefault: true }, { path: 'tasks/editor', name: 'TaskEditorComponent', component: TaskEditorComponent }, { path: 'timer/...', name: 'TimerComponent', component: TimerComponent }]) export default class AppComponent {}
The new redirecting route just needs a string path property and a redirectTo property declaring the array of named routes we want to redirect all the requests to.
Note
At the time of this writing, the rotue definitions in the new Router still do not implement support for the redirectTo
property. Please check the online documentation for a more up-to-date status on the subject.