Updating Your Angular 2 App From RC4 to RC5: A Practical Guide (Also works for RC6, RC7 and Angular 2 Final)
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.
Updating Your Angular 2 App to RC 5 Step by Step
Here’s what you’ll need to do:
- Update your package.json to refer to the new version
2.0.0-rc.5
and install the updated packages withnpm install
- Update the bootstrapping and wrap your app in an
NgModule
- Move your
directive
andpipe
declarations from@Component
to@NgModule
- Optionally move your
providers
declarations from@Component
to@NgModule
- Declare your module dependencies (
FormsModule
,HttpModule
, etc) - 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:
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 modulesdeclarations
: The component and directives that are part of your applicationboostrap
: The root component of your appproviders
: 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:
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:
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:
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:
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:
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:
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:
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
:
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:
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
:
// - 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
:
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?
- Angular 2 RC5 Announcement at the Angular 2 Team Blog
- Angular 2 RC4 to RC5 Migration Guide
- Complete Angular 2 RC5 Changelog
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
:
import {DeprecatedFormsModule} from @angular/common;
@NgModule({
imports: [ BrowserModule, DeprecatedFormsModule ],
declarations: [ AppComponent, PeopleListComponent, PersonDetailsComponent],
bootstrap: [ AppComponent ]
})
export class AppModule { }
Written by Jaime González García , dad, husband, software engineer, ux designer, amateur pixel artist, tinkerer and master of the arcane arts. You can also find him on Twitter jabbering about random stuff.