Angular is on a six-month release cadence, which means you need to stay on top of them in your own projects. The last thing you want to do is wake up one day and find that the latest version was just released and you are stuck on a version from two and a half years ago. Fortunately, the Angular team has made it very easy to upgrade.
Official Upgrade Guidance
Did you know that Angular offers official upgrade guidance? If you have not seen the website, take a look here at the Angular Upgrade Guide.
My Project
The project I am upgrading is the demo application presented in my book and course on Ionic and Angular development. You can find both at Gumroad.
The application itself is an Ionic app, written to run equally well on both desktop and mobile devices.
At the time I began, it was an Angular 8 app that had been automatically generated by the Ionic CLI.
You can see the app as it was just before I started the upgrade at its public GitHub repo.
I do not want it to fall too far behind, so I decided I should upgrade it to Angular 10 and make sure it works.
Angular 8.x to 8.y
The first thing I did was make sure the project was on the latest version of Angular 8. I did this by first making sure I had a clean repo (I did) and then entering the following command (and its output):
npx ng update @angular/cli@8 @angular/core@8
The installed Angular CLI version is older than the latest stable version.
Installing a temporary version to perform the update.
Installing packages for tooling via npm.
Installed packages for tooling via npm.
Using package manager: 'npm'
Collecting installed dependencies...
Found 35 dependencies.
Fetching dependency metadata from registry...
Package '@angular/core' is already up to date.
Updating package.json with dependency @angular-devkit/build-angular @ "0.803.29" (was "0.803.25")...
Updating package.json with dependency @angular/cli @ "8.3.29" (was "8.3.25")...
UPDATE package.json (1619 bytes)
✔ Packages installed successfully.
I specifically asked the CLI to update to @angular/cli and @angular/core version 8, which will update to the most recent minor/revision available to that major version.
I used npx
to use the local Angular CLI version in the project. As you can see from the warning that it had its own idea and installed a temporary version to perform the update.
Apparently I was pretty close to current on Angular 8. It did not do much.
Next, I committed the code. Angular will not update on an unclean working repo.
git commit -am "Updated to latest Angular 8"
[master e9e9c71] Updated to latest Angular 8
2 files changed, 3429 insertions(+), 2214 deletions(-)
You can compare what changed by reviewing the diff on GitHub. Again, not much happened.
Angular 8 to Angular 9
Next, I upgraded the project to Angular 9, with this command (it should look familiar).
npx ng update @angular/cli@9 @angular/core@9
This command did a lot more than the last one because now we are looking at an update of a major version.
Here is the output:
The installed Angular CLI version is older than the latest stable version.
Installing a temporary version to perform the update.
Installing packages for tooling via npm.
Installed packages for tooling via npm.
Using package manager: 'npm'
Collecting installed dependencies...
Found 35 dependencies.
Fetching dependency metadata from registry...
Updating package.json with dependency @angular-devkit/build-angular @ "0.901.12" (was "0.803.29")...
Updating package.json with dependency @angular/cli @ "9.1.12" (was "8.3.29")...
Updating package.json with dependency @angular/compiler @ "9.1.12" (was "8.2.14")...
Updating package.json with dependency @angular/compiler-cli @ "9.1.12" (was "8.2.14")...
Updating package.json with dependency @angular/language-service @ "9.1.12" (was "8.2.14")...
Updating package.json with dependency typescript @ "3.8.3" (was "3.4.5")...
Updating package.json with dependency @angular/common @ "9.1.12" (was "8.2.14")...
Updating package.json with dependency @angular/core @ "9.1.12" (was "8.2.14")...
Updating package.json with dependency @angular/forms @ "9.1.12" (was "8.2.14")...
Updating package.json with dependency @angular/platform-browser @ "9.1.12" (was "8.2.14")...
Updating package.json with dependency @angular/platform-browser-dynamic @ "9.1.12" (was "8.2.14")...
Updating package.json with dependency @angular/router @ "9.1.12" (was "8.2.14")...
Updating package.json with dependency zone.js @ "0.10.3" (was "0.9.1")...
UPDATE package.json (1620 bytes)
✔ Packages installed successfully.
**Executing migrations of package '@angular/cli'**
❯ Update an Angular CLI project to version 9.
UPDATE angular.json (5386 bytes)
Migration completed.
**Executing migrations of package '@angular/core'**
❯ Static flag migration.
Removes the `static` flag from dynamic queries.
As of Angular 9, the "static" flag defaults to false and is no longer required for your view and content queries.
Read more about this here: https://v9.angular.io/guide/migration-dynamic-flag
Migration completed.
❯ Missing @Injectable and incomplete provider definition migration.
In Angular 9, enforcement of @Injectable decorators for DI is a bit stricter and incomplete provider definitions behave differently.
Read more about this here: https://v9.angular.io/guide/migration-injectable
Migration completed.
❯ ModuleWithProviders migration.
In Angular 9, the ModuleWithProviders type without a generic has been deprecated.
This migration adds the generic where it is missing.
Read more about this here: https://v9.angular.io/guide/migration-module-with-providers
Migration completed.
❯ Renderer to Renderer2 migration.
As of Angular 9, the Renderer class is no longer available.
Renderer2 should be used instead.
Read more about this here: https://v9.angular.io/guide/migration-renderer
Migration completed.
❯ Undecorated classes with decorated fields migration.
As of Angular 9, it is no longer supported to have Angular field decorators on a class that does not have an Angular decorator.
Read more about this here: https://v9.angular.io/guide/migration-undecorated-classes
Migration completed.
❯ Undecorated classes with DI migration.
As of Angular 9, it is no longer supported to use Angular DI on a class that does not have an Angular decorator.
Read more about this here: https://v9.angular.io/guide/migration-undecorated-classes
Migration completed.
Your project has been updated to Angular version 9!
For more info, please see: https://v9.angular.io/guide/updating-to-version-9
There were some warnings, which I will take care of eventually. For now, they did not affect the application. I then committed the changes.
git commit -am"Upgraded to Angular 9"
[master ba20fd7] Upgraded to Angular 9
3 files changed, 3695 insertions(+), 2830 deletions(-)
The GitHub diff is here.
The only thing of note that it changed was that it added this bit of JSON to the angular.json file:
{
"type": "anyComponentStyle",
"maximumWarning": "6kb"
}
Angular 9 to Angular 10
Finally, I let the CLI upgrade from Angular 9 to Angular 10.
npx ng update @angular/cli @angular/core
The installed local Angular CLI version is older than the latest stable version.
Installing a temporary version to perform the update.
Installing packages for tooling via npm.
Installed packages for tooling via npm.
Using package manager: 'npm'
Collecting installed dependencies...
Found 35 dependencies.
Fetching dependency metadata from registry...
Updating package.json with dependency @angular-devkit/build-angular @ "0.1001.7" (was "0.901.12")...
Updating package.json with dependency @angular/cli @ "10.1.7" (was "9.1.12")...
Updating package.json with dependency @angular/compiler @ "10.1.6" (was "9.1.12")...
Updating package.json with dependency @angular/compiler-cli @ "10.1.6" (was "9.1.12")...
Updating package.json with dependency @angular/language-service @ "10.1.6" (was "9.1.12")...
Updating package.json with dependency typescript @ "4.0.3" (was "3.8.3")...
Updating package.json with dependency @angular/common @ "10.1.6" (was "9.1.12")...
Updating package.json with dependency @angular/core @ "10.1.6" (was "9.1.12")...
Updating package.json with dependency @angular/forms @ "10.1.6" (was "9.1.12")...
Updating package.json with dependency @angular/platform-browser @ "10.1.6" (was "9.1.12")...
Updating package.json with dependency @angular/platform-browser-dynamic @ "10.1.6" (was "9.1.12")...
Updating package.json with dependency @angular/router @ "10.1.6" (was "9.1.12")...
UPDATE package.json (1620 bytes)
✔ Packages installed successfully.
**Executing migrations of package '@angular/core'**
❯ Missing @Injectable and incomplete provider definition migration.
As of Angular 9, enforcement of @Injectable decorators for DI is a bit stricter and incomplete provider definitions behave differently.
Read more about this here: https://v9.angular.io/guide/migration-injectable
Migration completed.
❯ ModuleWithProviders migration.
As of Angular 10, the ModuleWithProviders type requires a generic.
This migration adds the generic where it is missing.
Read more about this here: https://v10.angular.io/guide/migration-module-with-providers
Migration completed.
❯ Undecorated classes with Angular features migration.
In version 10, classes that use Angular features and do not have an Angular decorator are no longer supported.
Read more about this here: https://v10.angular.io/guide/migration-undecorated-classes
Migration completed.
As before, I committed the code and checked the behavior of the application.
git commit -am"Upgraded to Angular 10"
[master a0114c1] Upgraded to Angular 10
2 files changed, 1945 insertions(+), 897 deletions(-)
The GitHub diff is here. As you can see, it only updated packages. Though it showed many of the same warnings, it did not find anything in the code it needed to migrate.
As I reviewed those migration guides, I also did not find any issues to update. I cannot confirm this, but I believe that the Ionic team keeps on top of these things and uses the latest Angular guidance in their application generators.
Upgrading to Angular 11
A few months have gone by since I originally wrote this article. Angular 11.1 was just released, so I thought it would be a good idea to update both the article and the application.
Angular 10.2
First, following the same pattern as before, I wanted to ensure I was on the latest version of Angular 10. I did that quickly and without error.
npx ng update @angular/cli@10 @angular/core@10
The installed local Angular CLI version is older than the latest stable version.
Installing a temporary version to perform the update.
Installing packages for tooling via npm.
Installed packages for tooling via npm.
Using package manager: 'npm'
Collecting installed dependencies...
Found 35 dependencies.
Fetching dependency metadata from registry...
Updating package.json with dependency @angular-devkit/build-angular @ "0.1002.1" (was "0.1002.0")...
Updating package.json with dependency @angular/cli @ "10.2.1" (was "10.2.0")...
Updating package.json with dependency @angular/compiler @ "10.2.4" (was "10.2.3")...
Updating package.json with dependency @angular/compiler-cli @ "10.2.4" (was "10.2.3")...
Updating package.json with dependency @angular/language-service @ "10.2.4" (was "10.2.3")...
Updating package.json with dependency @angular/common @ "10.2.4" (was "10.2.3")...
Updating package.json with dependency @angular/core @ "10.2.4" (was "10.2.3")...
Updating package.json with dependency @angular/forms @ "10.2.4" (was "10.2.3")...
Updating package.json with dependency @angular/platform-browser @ "10.2.4" (was "10.2.3")...
Updating package.json with dependency @angular/platform-browser-dynamic @ "10.2.4" (was "10.2.3")...
Updating package.json with dependency @angular/router @ "10.2.4" (was "10.2.3")...
UPDATE package.json (1620 bytes)
✔ Packages installed successfully.
This made no code changes, which I did not really expect.
TSLint and Codelyzer
This is where I finally ran into some minor trouble. My initial migration to Angular 11 failed due to some incompatible peer dependencies with Codelyzer.
Angular 11 finally does away with TSLint and Codelyzer for its linting tools, so I had to remove them before proceeding.
npm uninstall -D codelyzer tslint
git commit -am"Removed tslint and codelyzer"
This went smoothly, and simply removed those two entries from my package.json file.
At that point, the ng lint
command not longer worked. Executing it resulted in this helpful error message:
TSLint's support is discontinued and we're deprecating its support in Angular CLI.
To opt-in using the community driven ESLint builder, see: https://github.com/angular-eslint/angular-eslint#migrating-from-codelyzer-and-tslint.
I will leave that for another time, but it seems as though it will be reasonably straightforward.
Angular 11
Now it was time to upgrade from Angular 10.2 to Angular 11.1. The command and its output are shown below:
npx ng update @angular/cli@11 @angular/core@11
The installed local Angular CLI version is older than the latest stable version.
Installing a temporary version to perform the update.
Installing packages for tooling via npm.
Installed packages for tooling via npm.
Using package manager: 'npm'
Collecting installed dependencies...
Found 33 dependencies.
Fetching dependency metadata from registry...
Updating package.json with dependency @angular-devkit/build-angular @ "0.1101.0" (was "0.1002.1")...
Updating package.json with dependency @angular/cli @ "11.1.0" (was "10.2.1")...
Updating package.json with dependency @angular/compiler @ "11.1.0" (was "10.2.4")...
Updating package.json with dependency @angular/compiler-cli @ "11.1.0" (was "10.2.4")...
Updating package.json with dependency @angular/language-service @ "11.1.0" (was "10.2.4")...
Updating package.json with dependency karma @ "5.2.3" (was "4.1.0")...
Updating package.json with dependency protractor @ "7.0.0" (was "5.4.3")...
Updating package.json with dependency @angular/common @ "11.1.0" (was "10.2.4")...
Updating package.json with dependency @angular/core @ "11.1.0" (was "10.2.4")...
Updating package.json with dependency @angular/forms @ "11.1.0" (was "10.2.4")...
Updating package.json with dependency @angular/platform-browser @ "11.1.0" (was "10.2.4")...
Updating package.json with dependency @angular/platform-browser-dynamic @ "11.1.0" (was "10.2.4")...
Updating package.json with dependency @angular/router @ "11.1.0" (was "10.2.4")...
UPDATE package.json (1568 bytes)
✔ Packages installed successfully.
** Executing migrations of package '@angular/core' **
❯ In Angular version 11, the type of `AbstractControl.parent` can be `null` to reflect the runtime value more accurately.
This migration automatically adds non-null assertions to existing accesses of the `parent` property on types like `FormControl`, `FormArray` and `FormGroup`.
Migration completed.
❯ ViewEncapsulation.Native has been removed as of Angular version 11.
This migration replaces any usages with ViewEncapsulation.ShadowDom.
Migration completed.
❯ NavigationExtras omissions migration.
In version 11, some unsupported properties were omitted from the `extras` parameter of the `Router.navigateByUrl` and `Router.createUrlTree` methods.
Migration completed.
❯ Updates the `initialNavigation` property for `RouterModule.forRoot`.
Migration completed.
❯ NavigationExtras.preserveQueryParams has been removed as of Angular version 11.
This migration replaces any usages with the appropriate assignment of the queryParamsHandling key.
Migration completed.
❯ The default value for `relativeLinkResolution` is changing from 'legacy' to 'corrected'.
This migration updates `RouterModule` configurations that use the default value to
now specifically use 'legacy' to prevent breakages when updating.
UPDATE src/app/app-routing.module.ts (782 bytes)
Migration completed.
❯ `async` to `waitForAsync` migration.
The `async` testing function has been renamed to `waitForAsync` to avoid confusion with the native `async` keyword.
UPDATE src/app/app.component.spec.ts (2562 bytes)
UPDATE src/app/home/home.page.spec.ts (647 bytes)
UPDATE src/app/roster/roster.page.spec.ts (661 bytes)
UPDATE src/app/student-info/student-info.page.spec.ts (697 bytes)
Migration completed.
❯ Removes `canActivate` from a `Route` config when `redirectTo` is also present.
Migration completed.
As you can see, there are some things going on here other than simply updating some libraries.
- The
async
testing function has been renamed towaitForAsync
to avoid confusion with the nativeasync
keyword. The migration tool converted them automatically. - In my app's routing module (app-routing.module.ts), the migration added
relativeLinkResolution: 'legacy'
to theconfig:ExtraOptions
parameter in theRouterModule.forRoot
call.
As with the rest of the migrations, this one made no changes to the app's functionality, so I went ahead and committed the code.
Summary
Will your upgrade go as smoothly? I cannot guarantee it, of course. Honestly, the application is modest enough that I did not expect any problems.
As a convenient summary, here the commands I used (without the command output being shown).
npx ng update @angular/cli@8 @angular/core@8
git commit -am "Updated to latest Angular 8"
npx ng update @angular/cli@9 @angular/core@9
npx ng generate component Loading --spec=false --dry-run\n
git commit -am"Upgraded to Angular 9"
npx ng update @angular/cli @angular/core
git commit -am"Upgraded to Angular 10"
npx ng update @angular/cli@10 @angular/core@10
git commit -am"Upgraded to Angular 10.2.4"
npm uninstall -D codelyzer tslint
npx ng update @angular/cli@11 @angular/core@11
git commit -am"Upgraded to Angular 11.1"
As I said, your experience may not be the same as mine. You may not even need npx
. I hope this article helped you, even a little, and I wish you every success in your own endeavors.
Discount Code
If you are interested in either the book or the course on building web and mobile Apps with Angular and Ionic, here is a code good for 25% off the regular price.
- Book: https://gum.co/NlVUr/blog25
- Course: https://gum.co/FyZHi/blog25