Angular2 Quicky - Async Routes

Angular2 Quicky - Async Routes

Angular2 ships with it's own component router. The router makes it easy to compose complex applications from a bunch of components. Multiple routers could easily be nested to achieve almost every requirement for a SPA. When using regular routes, all files from your SPA will be transferred to the client as soon as RouteConfig is interpreted by the JavaScript engine of your browser.

With growing apps, you may change this behaviour. Clients should only download those files that are required for the current use case. Or you would initially transfer only those files required to make the core functionality of your app work. Optional components and templates should only be transferred if users request those components explicitly.

This can easily be achieved using AsyncRoutes. The AsyncRoute type is part of the ComponentRouter, by using the AsyncRoute you can specify a custom loader function. The loader function is responsible for requesting the component from the backend. Take the following example as starting point where all routes are loaded in traditional way during initial RouteConfig execution

import {Component} from 'angular2/core';  
import {ROUTER_DIRECTIVES, RouteConfig} from 'angular2/router';  
import {ProductsComponent} from '../products/products';  
import {CustomersComponent} from '../customers/customers';  
import {SupportCenterComponent} from '../support/supportcenter';  
import {AboutComponent} from "../about/about";

@Component({
  selector: 'async-routes-app',
  directives: [ROUTER_DIRECTIVES],
  templateUrl: 'app/components/app/app.html'
})
@RouteConfig([
  {path: '/customers/...', name: 'Customers', useAsDefault: true, component: CustomersComponent},
  {path: '/products/...', name: 'Products', component: ProductsComponent},
  {path: '/support/...', name: 'SupportCenter', component: SupportCenterComponent},
  {path: '/about', name: 'About', component: AboutComponent}     
])
export class AppComponent {

}

To get their work done, users need almost every time forms and lists underneath Customers and Products, so it's a good idea to keep those routes as they are. But SupportCenter and About routes aren't business critical, only a few users may use forms and lists sitting behind those routes.

That said it's a good idea to use AsyncRoutes to load those artefacts on demand. Let's change our previous code to match these new requirements.

import {Component} from 'angular2/core';  
import {ROUTER_DIRECTIVES, RouteConfig, AsyncRoute} from 'angular2/router';  
import {ProductsComponent} from '../products/products';  
import {CustomersComponent} from '../customers/customers';

@Component({
  selector: 'async-routes-app',
  directives: [ROUTER_DIRECTIVES],
  templateUrl: 'app/components/app/app.html'
})
@RouteConfig([
  {path: '/customers/...', name: 'Customers', useAsDefault: true, component: CustomersComponent},
  {path: '/products/...', name: 'Products', component: ProductsComponent},
  new AsyncRoute({
    path: '/support/...',
    name: 'SupportCenter',
    loader: () => System.import('app/components/support/supportcenter').then(m=> m.SupportCenterComponent)
  }),
  new AsyncRoute({
    path: '/about',
    name: 'About',
    loader: () => {
      return System.import('app/components/about/about')
        .then(m=> m.AboutComponent);
    }
  })
])
export class AppComponent {

}

Here are a few interesting things happening now. First we've added AsyncRoute to the import statements and pulled it from angular2/router. We've removed the import statements for both AboutComponent and SupportCenterComponent because those Components will be loaded dynamically now.

The biggest changes have been made to @RouteConfig. We removed two regular routes and introduced two AsyncRoute implementations.

Take a look at both variants used for the loader function. For SupportCenter our expression is implicitly returning the Promise provided by System.import().then(). It's important that you remember to return the promise! That's why I've added the verbose syntax for the second AsyncRoute.

Once SystemJS has finished loading the files from the given URL, we've to specify the Component or artefact we want to use from our bundle. In both cases we're - of course - using our required components (m.SupportCenterComponent and m.AboutComponent).

Angular2 will assign the payload from the promise to the route's component property once the promise is resolved.

When transpiling those TypeScript sources to JavaScript right now, you may receive some errors because TSD doesn't know System.import. Unfortunately there are no definition files available for SystemJS at this point in time. Either we sit down and write our own definition file right now - which is time consuming - or we tell TSD to track System as variable of type any. It's not the cleanest solution, but it's the simplest to get things working now.

To do so, add the following statement to your TypeScript file (I prefer such statements underneath all import statements.)

declare var System:any;  

There is more. Check out Minko's post, he describes in detail how to create virtual proxies to push async loading to another level, but before you move on leave a comment and share what you've done so far using Angular2's component router.

Comments

comments powered by Disqus