DEV Community

Cover image for The new RouterOutlet events in Angular 13
Dmitrij Kuba
Dmitrij Kuba

Posted on

The new RouterOutlet events in Angular 13

It's common practice in angular space to cache reused components with the help of RouteReuseStrategy:

import { Injectable } from '@angular/core';
import {
  ActivatedRouteSnapshot,
  DetachedRouteHandle,
  Route,
  RouteReuseStrategy,
} from '@angular/router';

@Injectable()
export class CustomReuseStrategy extends RouteReuseStrategy {
  private pool = new WeakMap<Route, DetachedRouteHandle>();

  public shouldAttach(route: ActivatedRouteSnapshot): boolean {
    return !!this.pool.get(route.routeConfig);
  }

  public retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle | null {
    return this.pool.get(route.routeConfig);
  }

  public shouldDetach(route: ActivatedRouteSnapshot): boolean {
    return (
      route.routeConfig.data?.shouldReuse && !this.pool.get(route.routeConfig)
    );
  }

  public store(
    route: ActivatedRouteSnapshot,
    handle: DetachedRouteHandle | null
  ) {
    this.pool.set(route.routeConfig, handle);
  }

  public shouldReuseRoute(
    future: ActivatedRouteSnapshot,
    curr: ActivatedRouteSnapshot
  ): boolean {
    return future.routeConfig === curr.routeConfig;
  }
}
Enter fullscreen mode Exit fullscreen mode

then new CustomReuseStrategy should be provided to the module:

import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { ChildComponent } from './child/child.component';
import { CustomReuseStrategy } from './route-reuse-strategy';
import { NgModule } from '@angular/core';
import { RouteReuseStrategy, RouterModule } from '@angular/router';

@NgModule({
  imports: [
  ...
  providers: [
    {
      provide: RouteReuseStrategy,
      useClass: CustomReuseStrategy,
    },
  ],
})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode

at this point angular router module registers the new strategy to reuse routes (from cache) and the instance of the strategy is available under Router#routeReuseStrategy.

Let’s define few routes to demonstrate the new RouterOutlet events (/c marked to reuse on the next activation):

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { RouteReuseStrategy, RouterModule } from '@angular/router';
import { ChildComponent } from './child/child.component';
import { CustomReuseStrategy } from './route-reuse-strategy';

@NgModule({
  imports: [
    RouterModule.forRoot([
      {
        path: 'a',
        component: ChildComponent,
      },
      {
        path: 'b',
        component: ChildComponent,
      },
      {
        path: 'c',
        component: ChildComponent,
        data: {
          shouldReuse: true,
        },
      },
    ]),
    BrowserModule,
  ],
  declarations: [AppComponent, ChildComponent],
  bootstrap: [AppComponent],
  providers: [
    {
      provide: RouteReuseStrategy,
      useClass: CustomReuseStrategy,
    },
  ],
})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode

and the app.component.html looks like (just a few links and the router-outlet with new attach/detach events):

<a routerLink="/a">navigate to /a</a> <br />
<a routerLink="/b">navigate to /b</a> <br />
<a routerLink="/c">navigate to /c</a> <br />
<router-outlet
  (activate)="onActivate($event)"
  (deactivate)="onDeactivate($event)"
  (attach)="onAttach($event)"             <--- the new event
  (detach)="onDetach($event)"             <--- the new event
></router-outlet>
Enter fullscreen mode Exit fullscreen mode

An attach event emits every time when the RouteReuseStrategy instructs the outlet to reattach the subtree, and the detach event emits when the RouteReuseStrategy instructs the outlet to detach the subtree. So attach event will be emitted on the next re-navigation to the /c url instead of activate one, because the component was attached from cache and not instantiated.

Sources:

Top comments (0)