Ashley Sheridan​

Updating Angular from 2 to 4

Posted on


I recently needed to upgrade an Angular project from 2.4.1 to the latest (at the time of writing) version of 4.2. All the typical guides detail how to update the core @angular packages in the package.json, but stop there, omitting the other things that also need to be done to ensure the project/application still works as intended. This is what I found as I was performing my update:

Updating the Core Packages

The first step that all the standard guides give is to upgrade the core packages:

npm install @angular/{animations,common,compiler,compiler-cli,core,forms,http,platform-browser,platform-browser-dynamic,platform-server,router}@4.0.0 typescript@latest --save

Despite the notice that this is the command for only Linux & Mac computers, it will work on Windows too if you're using a Bash shell, e.g. GitBash. That's because of the brace expansion here, which turns things like @angular/{animations,common}@4.0.0 into @angular/animations@4.0.0 @angular/common@4.0.0 (the full explanation is a little more involved, but you get the idea.)

If you prefer to manually update your package.json file, then just change the version numbers for the packages that begin with @angular to point to version 4.2.0.

At this point I tried to build the project and ran into more than a few problems, because other things also need to be updated with it, which isn't particularly clear in other guides.

Angular-CLI Update

The first stumbling block was the Angular CLI, which had been still running in at 1.0.0-beta.24 version, which results in this error:

Unable to find "@angular/cli" in devDependencies. The package "angular-cli" has been deprecated and renamed to "@angular/cli". Please take the following steps to avoid issues: "npm uninstall --save-dev angular-cli" "npm install --save-dev @angular/cli@latest"

You can update this either by manually editing the package.json to point the package to the latest version (1.3.2 at time of writing) or uninstall/reinstall via the CLI:

npm uninstall --save-dev angular-cli && npm install --save-dev @angular/cli@latest

Note that if you update this package manually, you'll need to change the name of it from angular-cli to @angular/cli, as this changed in the current releases.

Updating RXJS

The next issue I ran into was an error coming from RXJS when attempting to build the vendor bundle:

ERROR in [project path]/node_modules/rxjs/Subject.d.ts (16,22): Class 'Subject<T>' incorrectly extends base class 'Observable<T>'. Types of property 'lift' are incompatible. Type '<R>(operator: Operator<T, R>) => Observable<T>' is not assignable to type '<R>(operator: Operator<T, R>) => Observable<R>'. Type 'Observable<T>' is not assignable to type 'Observable<R>'. Type 'T' is not assignable to type 'R'.

I found that updating rxjs to ^5.0.1 resolved this problem, but presumably any of the 5.x series will be sufficient.

Environment Configuration

The next problem offers the exact fix in the output of the error, and it's a very easy fix:

Environment configuration does not contain "environmentSource" entry. A new environmentSource entry replaces the previous source entry inside environments. To migrate angular-cli.json follow the example below: Before: "environments": { "source": "environments/environment.ts", "dev": "environments/environment.ts", "prod": "environments/" } After: "environmentSource": "environments/environment.ts", "environments": { "dev": "environments/environment.ts", "prod": "environments/" }

You'll only run into this if you're using the Angular CLI tool to set up and manage your application, so if you don't use that, you can ignore this step entirely.

Duplicate Webpack & AotPlugin Instance of Wrong Class

This one caused quite a bit of frustration, as the help text offered a useless solution:

ERROR in ./src/test.ts Module build failed: Error: AotPlugin was detected but it was an instance of the wrong class. This likely means you have several @ngtools/webpack packages installed. You can check this with `npm ls @ngtools/webpack`, and then remove the extra copies. at Object.ngcLoader ([project path]\node_modules\@ngtools\webpack\src\loader.js:358:19)

I tried updating webpack, adding the extra plugins that it might be using, and various other things, but I couldn't get rid of this error. Turns out that the solution was just to remove it entirely from the package.json. The duplication conflict was because there's another version being used as a sub-module by something else, and it's a completely different one to this. Hopefully the Angular team can resolve this one better soon, or at least have the error message give more useful information.

Karma Cannot Find Module Errors

ERROR [config]: Error in config file! { Error: Cannot find module 'angular-cli/plugins/karma'

The next few issues all shared a similar cause; the angular-cli package had been moved in Angular 4. This meant that the paths within the karma.conf.js file needed to be updated. Fortunately, you can just run a find/replace on the whole file, and replace every instance of angular-cli with @angular/cli.

Cannot Find Name 'ComponentFixture'

If you're using the ComponentFixture in your unit tests, then you'll most likely see this error:

ERROR in [project path]/[test file].spec.ts (41,15): Cannot find name 'ComponentFixture'.

Previously, with Angular 2, the ComponentFixture class was already included via another package, so if you had used the Typescript type hinting to indicate that a certain variable was a ComponentFixture it would just work. As it's since been moved, Angular no longer knows where to find it, and so it generates an error based on the now broken type hinting. The package can just be added to the list of imports from @angular/core/testing wherever you're using it, like so:

import { ComponentFixture, TestBed, inject } from '@angular/core/testing';


By the end of that, the tests and build were all running as expected, with some minor adjustments for old arguments left in method calls that were no longer applicable as these caused some warnings at build time. Builds and tests take about the same amount of time to run, but I've noticed that the vendor.bundle.js file was quite a bit smaller (about 92% of the size of the original generated by Angular 2). Given that this was the largest of the 3 main bundles by far, it was nice to see such a large chunk taken off of the final file size.