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.