barbarian meets coding

WebDev, UX & a Pinch of Fantasy

Updating Your Angular 2 App From RC4 to RC5: A Practical Guide (Also Works for RC6, RC7 and Angular 2 Final)

| Comments

UPDATE 14th Sep 2016: This guide also works for updating your apps to RC6, RC7 and Angular 2 Final.

Earlier this week we got the RC5 release of Angular 2, a version of Angular 2 that “represents the expected public API for our 2.0.0 release, including router and forms APIs” according to Rob Wormald of the Angular 2 team. Yey!

I’ve spent this week updating my getting started series and compiled the most important changes you need to do to update your application from Angular 2 RC4 to RC5 including the new router.

Angular 2 Getting Started

Updating Your Angular 2 App to RC 5 Step by Step

Here’s what you’ll need to do:

  1. Update your package.json to refer to the new version 2.0.0-rc.5 and install the updated packages with npm install
  2. Update the bootstrapping and wrap your app in an NgModule
  3. Move your directive and pipe declarations from @Component to @NgModule
  4. Optionally move your providers declarations from @Component to @NgModule
  5. Declare your module dependencies (FormsModule, HttpModule, etc)
  6. Update the router.

Before we start, know that Rob has written a migration guide with helpful information about updating your package.json and creating your NgModule. You may want to take a look at it before or after reading this article. Additionally, if you want to have an Angular 2 reference application in RC5 you are welcome to take a look at this sample.

2. Update The Bootstrapping and Wrap Your App in an NgModule

Angular 2 RC5 brings the concept of an Angular Module. Weren’t we using ES 6 modules already? You may be asking yourself, and Yes, we do. Angular NgModule is a way to define our application dependencies so you don’t need to do it repeatedly on a per-component basis. This means that the directives and providers that you used to put at the @Component level can now be moved to the module level saving you some precious extra keystrokes.

You’ll need to create a new file app.module.ts where to place your NgModule that is going to describe your application and its dependencies. Here you have a bare bones module:

1
2
3
4
5
6
7
8
9
10
11
12
import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent }  from './app.component';

@NgModule({
  imports: [ BrowserModule],       // module dependencies
  declarations: [ AppComponent],   // components and directives
  bootstrap: [ AppComponent ],     // root component
  providers: []                    // services
})
export class AppModule { }

The NgModule decorator uses the following metadata:

  • imports: An array of modules that you want to use in your application such as the forms or http modules
  • declarations: The component and directives that are part of your application
  • boostrap: The root component of your app
  • providers: Services that you want to make available module-wide

Having created the app.module.ts you’ll need to update your bootstrapping code in main.ts from:

1
2
3
4
5
6
7
8
9
10
11
12
13
import { bootstrap }    from '@angular/platform-browser-dynamic';
import { disableDeprecatedForms, provideForms } from '@angular/forms';
import { APP_ROUTER_PROVIDERS } from './app.routes';
import { HTTP_PROVIDERS } from '@angular/http';
import { AppComponent } from './app.component';

bootstrap(AppComponent, [
  APP_ROUTER_PROVIDERS,         // Routing Config!
  HTTP_PROVIDERS,               // Http
  disableDeprecatedForms(),     // Disable old Forms API!
  provideForms()                // Use new Forms API!
])
 .catch((err: any) => console.error(err));

To:

1
2
3
4
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';

platformBrowserDynamic().bootstrapModule(AppModule);

Where we are using the newly defined AppModule.

3. Move your directives and pipe declarations from @Component to @NgModule

Before RC5, Angular 2 forced you to declare which components and directives you were going to use inside a component. You usually did this within the directives property of the @Component decorator:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { Component, OnInit } from '@angular/core';
import { Person } from './person';
import { PeopleService } from './people.service';
import { PeopleDetailsComponent } from './people-details.component';

@Component({
  selector: 'people-list',
  directives: [PeopleDetailsComponent],
  template: `
  <section>
    <!-- some template -->
  </section>
  `
})
export class PeopleListComponent implements OnInit{
  // code...
}

With RC5 you can move all these declarations to the NgModule and they’ll automagically be made available to all components inside your application:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent }  from './app.component';
import { PeopleListComponent } from './people-list.component';
import { PersonDetailsComponent } from './person-details.component';

@NgModule({
  imports: [ BrowserModule ],
  declarations: [ AppComponent, PeopleListComponent, PersonDetailsComponent], // here!
  bootstrap: [ AppComponent ],
  providers: []
})
export class AppModule { }

Which means that you can remove them from inside all your components:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { Component, OnInit } from '@angular/core';
import { Person } from './person';
import { PeopleService } from './people.service';

@Component({
  selector: 'people-list',
  template: `
  <section>
    <!-- some template -->
  </section>
  `
})
export class PeopleListComponent implements OnInit{
  // code...
}

This also applies to pipes that you would declare in the pipes property of the @Component decorator. Much in the same way as directives and components, you’ll need to move them to the declarations property of your new @NgModule decorator.

4. Optionally move your providers declarations from @Component to @NgModule

Similarly, you can move your services from your components to the NgModule if you want to use the same instance of a service module-wide:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent }  from './app.component';
import { PeopleListComponent } from './people-list.component';
import { PersonDetailsComponent } from './person-details.component';

import { PeopleService } from './people.service';

@NgModule({
  imports: [ BrowserModule ],
  declarations: [ AppComponent, PeopleListComponent, PersonDetailsComponent],
  bootstrap: [ AppComponent ],
  providers: [PeopleService]     // here!
})
export class AppModule { }

Note that if you want to have separate instances for different parts of your application, you can still use the providers property inside a component.

5. Declare your module dependencies (FormsModule, HttpModule, etc)

Prior to RC5, we used to import Angular 2 directives and services from different Angular 2 modules and referenced them directly inside the components where they were meant to be used. For instance, in order to use the routerLink we would import ROUTER_DIRECTIVES and add them to our directives array:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { Component, OnInit } from '@angular/core';
import { ROUTER_DIRECTIVES } from '@angular/router';
import { Person } from './person';
import { PeopleService } from './people.service';

@Component({
  selector: 'people-list',
  directives: [ROUTER_DIRECTIVES] ,
  template: `
    <!-- some template -->
  `
})
export class PeopleListComponent implements OnInit{
    // code
}

That is no longer necessary with RC5 where we express these dependencies in the NgModule. For instance, in this example below we’ll be using the FormsModule and the HttpModule:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';

import { AppComponent }  from './app.component';
import { PeopleListComponent } from './people-list.component';
import { PersonDetailsComponent } from './person-details.component';

@NgModule({
  imports: [ BrowserModule, FormsModule, HttpModule],
  declarations: [ AppComponent, PeopleListComponent, PersonDetailsComponent],
  bootstrap: [ AppComponent ]
})
export class AppModule { }

This means that you can remove all trace of these modules from your components directives and providers if you so wish. There’s no need for ROUTER_DIRECTIVES, ROUTER_PROVIDERS, HTTP_PROVIDERS, etc. All these are made available to you by declaring the module dependency in NgModule.

The routing module is a little bit special as we’ll see in the next section.

6. Update the router.

In RC4 the Angular team introduced the new component router. You may have a app.routes.ts file that describes the routes available in your application:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import { provideRouter, RouterConfig } from '@angular/router';
import { PeopleListComponent } from './people-list.component';
import { PersonDetailsComponent } from './person-details.component';

// Route config let's you map routes to components
const routes: RouterConfig = [
  // map '/persons' to the people list component
  {
    path: 'persons',
    component: PeopleListComponent,
  },
  // map '/persons/:id' to person details component
  {
    path: 'persons/:id',
    component: PersonDetailsComponent
  },
  // map '/' to '/persons' as our default route
  {
    path: '',
    redirectTo: '/persons',
    pathMatch: 'full'
  },
];

export const APP_ROUTER_PROVIDERS = [
  provideRouter(routes)
];

With RC5 (and the new version of the router which is RC1) there’s some changes that you’ll need to do. RouterConfig is now named Routes, you no longer import provideRouter but RouterModule and the way you export your application is slightly different. Here you can see an updated version of the previous app.routes.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// - Routes instead of RouteConfig
// - RouterModule instead of provideRoutes
import { Routes, RouterModule } from '@angular/router';

import { PeopleListComponent } from './people-list.component';
import { PersonDetailsComponent } from './person-details.component';

const routes: Routes = [
  {
    path: 'persons',
    component: PeopleListComponent,
  },
  {
    path: 'persons/:id',
    component: PersonDetailsComponent
  },
  {
    path: '',
    redirectTo: '/persons',
    pathMatch: 'full'
  },
];

// - Updated Export
export const routing = RouterModule.forRoot(routes);

Having defined your routing you can import it in your NgModule at app.module.ts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';

import { routing } from './app.routes';

import { AppComponent }  from './app.component';
import { PeopleListComponent } from './people-list.component';
import { PersonDetailsComponent } from './person-details.component';

import { PeopleService } from './people.service';

@NgModule({
  imports: [ BrowserModule, routing, FormsModule, HttpModule],
  declarations: [ AppComponent, PeopleListComponent, PersonDetailsComponent],
  bootstrap: [ AppComponent ],
  providers: [ PeopleService]
})
export class AppModule { }

And you’re all set! You should’ve been able to remove some redundant code from your components and now you should be able to run your application in RC5.

Victory!

Would you Like To Know More?

A Sidenote About the Old Forms API

In the previous version of Angular 2 RC4 the old forms API was opt-out, that is, you needed to specifically disable it in order to get access to the new forms API. In RC5 it’s the opposite, you need to opt-in to get access to the old api forms. You do this much in the same way as you get access to the new forms API through the FormsModule and @NgModule. That is, if you need to enable the old forms API in your application because you haven’t had the time to update it yet, you just need to import the DeprecatedFormsModule and add it to your imports:

1
2
3
4
5
6
7
8
import {DeprecatedFormsModule} from @angular/common;

@NgModule({
  imports: [ BrowserModule, DeprecatedFormsModule ],
  declarations: [ AppComponent, PeopleListComponent, PersonDetailsComponent],
  bootstrap: [ AppComponent ]
})
export class AppModule { }

Comments