Angular2: Auto Populate data for component on startup using Resolve feature of Angular 2 Routing

Standard

If we were using a real world api, there may be some delay in when the data we want to display gets returned. We don’t want to display a blank component until the data loads in this situation.

We’d like to pre-fetch data from the server so it’s ready the moment our route is activated. We’d also like to handle the situation where our data fails to load or some other error condition occurs. We want to delay rendering of our route component until all necessary data has been fetched or some action has occurred.

Resolve guard is an interface we can implement as a service to resolve route data synchronously or asynchronously.

Data services usually resolve our this issue, they are easy to implement, maintain and super fun to read. Solution is simple define setter/getter for particular variable in service and then import this service in multiple components to read or write value.

First of all setup resolve property in routing configuration file.

Bold code below shows this design guideline implementation

route-config.ts:

import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';

import { SearchMovieMainComponent } from './search-movies-main.component';
import { SearchMovieComponent } from './search-movie/search-movie.component';
import { SearchMovieListComponent } from './search-movie/search-list.component';
import { GetAllMoviesResolve } from './shared/service/get-all-movies.service';

@NgModule({
    imports: [
        RouterModule.forChild([
            {
                path: 'movie',
                component: SearchMovieMainComponent,
                children: [
                    {
                        path: 'searchMovie',
                        component: SearchMovieComponent
                    },
                    {
                        path: 'searchMovieList',
                        component: SearchMovieListComponent,
                        resolve: {
                            resolvedAllMovieList: GetAllMoviesResolve
                        }
                    }
                ]
            }
        ])
    ],
    exports: [
        RouterModule
    ]
})

export class SearchMoviesRoutingModule { }

I usually use service to implement resolve functionality specially if it needs to talk to server for receiving/pushing data. We want to be explicit about the data we are resolving, so we implement the Resolve interface with a type of MovieListModel. This lets us know that what we will resolve will match our MovieListModel model. We then implement the resolve method that supports a Promise, Observable or a synchronous return value.

Bold code below shows this design guideline implementation

get-all-movies.service.ts:

import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot } from '@angular/router';
import { Http, Response, Headers, RequestOptions } from '@angular/http';
import 'rxjs/add/operator/toPromise';
import { MovieListModel } from '../model/movie.model';

@Injectable()
export class GetAllMoviesResolve implements Resolve<MovieListModel> {
    headers: Headers;
    options: RequestOptions;

    constructor(private http: Http) {
        this.headers = new Headers({ 'Content-Type': 'application/json', 
                             'Accept': 'q=0.8;application/json;q=0.9' });
        this.options = new RequestOptions({ headers: this.headers });
    }

    resolve(route: ActivatedRouteSnapshot): Promise<MovieListModel> | boolean {            
        return this.http
            .get('api/Movie/GetAllMovies', this.options)
            .toPromise()
            .then((result: MovieListModel) => {
                return result;
            })
            .catch(error => console.log(error));
    }
}

Then I consume this resolve functionality inside my component code in ngOnIt() method.

Bold code below shows this design guideline implementation

resolve-service.ts:

import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute, Params } from '@angular/router';
import { MovieListModel } from '../shared/model/movie.model';


@Component({
    selector: 'search-movie-list',
    templateUrl: '../../Scripts/app/search-movies/search-movie-list.component.html'
})

export class SearchMovieListComponent implements OnInit {
    movieListModel: MovieListModel;

    constructor(
        private router: Router,
        private route: ActivatedRoute) {
    }

    ngOnInit() {
        //get data from resolve feature of routing
        this.route.data.forEach((data: { resolvedAllMovieList: MovieListModel }) => {
            this.movieListModel = data.resolvedAllMovieList;
        });
    }

}
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s