Angular Route Resolvers

Photo by @epicantus

Let's see Angular route resolvers in action.

You are creating an Angular Typescript application. You have created a few services for it that contain a couple of rest calls wrapped in promises. Now you are setting up routes, and you want to load certain resources before the page displays.

To load those resources before page display, you need to strap your route with a resolve parameter that references an injectable class called resolver. You can add a resolver to your Angular application the same way as you would add a regular service.

ng generate service resolver/projectResolver  

What Are Resolvers

Resolvers are classes that implement the resolve interface and have a resolve method which gets executed when the route is activated. The router expects the resolver to return either an Observable or a Promise. If the returning variable is an Observable the router stops the route navigation and listens for the observable to emit a 'complete' event before resuming navigation again. If the returning variable is a Promise then the router waits for the Promise to resolve.
A resolver looks similar to the following:

/// project resolver

import {Observable} from 'rxjs/Observable';  
import {ActivatedRouteSnapshot, Resolve} from '@angular/router';  
import {DynamoDBService} from '../service/ddb.service'  
import {Injectable} from "@angular/core";

//// notice how this implements the resolve interface
@Injectable()
export class ProjectResolverService implements Resolve<any>  {


/// I'm using serverless architecture so instead of having a service that makes ajax calls I have a service that makes database calls. 
  constructor(public db:DynamoDBService) { }

/// This is the resolve method that is inherited from the resolve interface. The resolve method returns an observable. When the observable emits 'complete' the route successfully navigates

/// To capture and use URL parameters you need to pass in `ActivatedRouteSnapshot`. 
  resolve(route:ActivatedRouteSnapshot):Observable<any>{

///My route has an /:projectId parameter. I use that identifier to load a specific resource from the database.
    let projectId = route.paramMap.get("projectId");

/// My service methods are wrapped in promises. I use the `Observable.fromPromise` method to change the methods returned promise to an observable. 

    return Observable.fromPromise(this.db.getProject(projectId));

  }
}

My resolver class has some extra fluff in it, but don't worry, I added comments to help break things down.

The route using the resolver looks similar to the following:

/// route.ts
import {RouterModule, Routes} from "@angular/router";  
import {ModuleWithProviders} from "@angular/core";

import {SecureHomeComponent} from "./secure/landing/securehome.component";

import { ProjectsComponent } from "./secure/projects/projects.component";

import { ProjectResolverService } from './service/project-resolver.service';



const secureHomeRoutes: Routes = [  
    {

        path: '',
        redirectTo: '/securehome',
        pathMatch: 'full'
    },
    {
        path: 'securehome', component: SecureHomeComponent, children: [
        {path: 'project/:projectId', component: ProjectComponent,
            resolve: {
///Notice here that I have the resolve paremeter and the resolver service.
                project: ProjectResolverService
            }
        }
    }
];

const routes: Routes = [  
    {
        path: '',
        children: [
            ...secureHomeRoutes
        ]
    },


];

export const appRoutingProviders: any[] = [];

export const routing: ModuleWithProviders = RouterModule.forRoot(routes);

I stripped away a lot of methods from the following service so that you don't have to scan through a lot of code to figure out what is happening. It should get the point across:

/// db.ts

import {Injectable} from "@angular/core";  
import {CognitoUtil} from "./cognito.service";  
import {environment} from "../../environments/environment";

import {Stuff} from "../secure/useractivity/useractivity.component";  
import * as AWS from "aws-sdk/global";  
import * as DynamoDB from "aws-sdk/clients/dynamodb";

@Injectable()
export class DynamoDBService {

    constructor(public cognitoUtil: CognitoUtil) {
        console.log("DynamoDBService: constructor");
    }

/// notice I'm returning a Promise here. That is ok because I'm doing Observable.fromPromise() which converts the promise to an Observable in the resolver. 
    getProject(projectId:string):Promise<any> {
        return new Promise((resolve, reject) => {

            var params = {
                TableName : 'projects',
                Key: {
                    projectId: projectId
                }
            };

            let userId = this.cognitoUtil.getCognitoIdentity();

            let clientParams: any = {
                params: {TableName: 'projects'}
            };
            if (environment.dynamodb_endpoint) {
                clientParams.endpoint = environment.dynamodb_endpoint;
            }
            var docClient = new DynamoDB.DocumentClient(clientParams);

            docClient.get(params, function(err, data) {
                if (err) reject(err);
                else console.log(data);

//I'm resolving the Promise here. 
                resolve(data);
            });


        })
    }

}