In this post, I will report what I learned in the migration of Angular applications v8 to v13.

In addition, I will show you the most common errors that you may encounter in the process as well as their respective solutions, indicating the sources when appropriate.

I spent 1 year and a half allocated in a division that worked with many applications and a suite of components built in Angular 8.


In the last 2 months of last year, we had meetings with the frontend working group where the subject of angular application migration has always been talked. Considered a critical issue, especially when we remembered the risk of recent cases such as the log4j vulnerability in Java and the discontinuity of the faker library that affected the projects in AngularJS, officially unsupported.

Then came an opportunity to work with technical debt. When we did this work the Angular version was 13 and because Google only supports up to 2 previous versions, Angular 2 through 10 were no longer supported.

Initial guidelines

We take the experience we had in migrating the component project from Angular 8 to 13. These are the possible migration approaches:

  1. Run a ng update with each version because it is not possible through it to move from version 8 straight to 13, so you need to run this command to migrate from 8 to 9, from 9 to 10, and so on. The advantage is that it converts the code automatically, but the process of going through so many versions ends up being more costly and therefore it is not advised in our case where there was a gap of 5 versions. It may be the most appropriate option for future migrations.

  2. Create a new project in Angular 13 and copy the fonts from the old project into Angular 8. Then solve the problems as they arise. This approach has been adopted for the migration of our component project and is what we recommend in this situation.

Migration roadmap

1) Install the latest version of Angular

npm install -g @angular/cli

2) Create a new project

ng new novo-projeto

3) Copy fonts from an old project

When copying the fonts it is advised to keep the new package.json that was generated to include and resolve the various dependencies on demand so that at the end of the process only the ones really needed are included, thus also meeting the best safety standards.

Update angular versions of the package.json of the subprojects (if any) as well.

4) Modify tsconfig.lib.json settings

Update the tsconfig.lib.json files in the subprojects (if any) to align with the current tsconfig.json files

"target": "es2017",   "module": "es2020",   "lib": [   "es2020",   "dom"   ]

Common configuration errors and solutions

Add the primeng to the package.json. This will be the most common error because it will occur for all the various dependencies that have not yet been included, such as bootstrap, ng-select, among others.

Try to always define the version of the dependency corresponding to the current version of Angular in the project.

Other situations that can be solved in this way are errors of the type:

Script file ____ does not exist.

or

An unhandled occurred exception: ENOENT: no such file or directory, lstat ______

Copy angular.json file from the old project

The build-ng-packagr has been discontinued.

Remove the build-ng-packagr from package.json and modify the tasks in angular.json, so that where there is @angular-devkit/build-ng-packagr:build replace for @angular-devkit/build-angular:ng-packagr

Resolves by modifying the version of ng-packagr in the package.json file to one corresponding to the Angular version of the project.

It is solved by adding the @angular/cdk to package.json.

Install the postcss and postcss-cli:

npm install postcss postcss-cli

Install chart and ng2-charts, which is a wrapper of the first, running:

npm install --save ng2-charts and npm install --save chart.js

Common errors involving code change and solutions

The earlier versions of primeng imports were like this:

import { Checkbox, MessageService, ConfirmDialogModule } from 'primeng/primeng';

References are now more specific, so they need to be corrected:

import { Checkbox } from 'primeng/checkbox';   import { MessageService } from 'primeng/api';   import { ConfirmDialogModule } from 'primeng/confirmdialog';

In order to support dynamic imports, add this line to tsconfig.json:

"module": "esnext",

Occurs after migration to rxjs 7. It can be solved by passing a fake value:

this.subject.next("");

It usually occurs after migration to Angular 12. It is solved by removing the "/" at the end of imports or replacing double quotes with a single. Example:

Before:

import { MsgCenterModule } from '@seu-componente/etc-client/';

After:

import { MsgCenterModule } from '@seu-componente/etc-client'; 

Before:

import { ABCEnum, XYZEnum } from "../../tabela.constants";

After:

import { ABCEnum, XYZEnum } from '../../tabela.constants'; 

Installing this library because even though it is not directly used in the application, it is necessary in some cases because it is referenced by the component used:

npm install dayjs --save

npm install inputmask --save

The typescript compiler is indicating that window.document.getElementById('content') may return NULL.

Since it is a migrated code that already worked previously, just add the ! to this code:

document.getElementById('content')!. innerHTML = '';

From primeng 9 some properties must be set in style. Therefore, in html, where it has

[width]="600" [minWidth]="200" change to [style]="{width: '600px', minWidth: '200px'}". Look at the brackets or a CSS-style setar error occurs.

This is because variables now need to have some type defined, so it is necessary at all points in the code to set them to any or for a more specific type.

A sometimes pointed solution is to set "noImplicitAny" to false in the tsconfig.json, but it is not advised.

Starting with version 2.7 the typescript compiler requires that the property/variable be initialized.

To solve this there are several approaches but we chose to add the "!" in the definition of the variable:

public container!: ViewContainerRef;

We did this because we have assurances that this was initialized because it worked on the previous project in Angular 8 since the previous code ran without errors during runtime.

Note: In tsconfig.json, compilerOptions can be defined as "strictPropertyInitialization": false, to ignore these errors and speed up this phase of migration, but it is recommended to go back to true later.

Very common when setting an interface property as optional, solved with "!" just like the previous error.

Newer versions of typescript are more rigid. In this case, you will need to modify the code to use the boolean primitive type.

It usually happens from version 9 of Angular. Add it to the public_api.ts the line corresponding to the class in question, in this example:

export * from './lib/msg-center/msg-center.component';

Change the code as follows, in this example:

static forRoot(): ModuleWithProviders<XYZClientModule>

Ensure that the value is returned to the code in question. It usually occurs in more extensive if-clauses.

Change the call to:

return throwError(() => response);

Install dependency:

npm i --save-dev @types/uuid

Modify the call in the code to:

import * as uuid from 'uuid';

You need to define the type of index that the object has by modifying the code to:

pagesStatus: {[index: string]:any} = {}

This can occur in html tag attributes. For example, where there were:

<p-fileUpload mode="advanced" name="demo[]" maxFileSize=1000000 multiple=true chooseLabel="Buscar arquivo" (onUpload)="onUpload($event)"></p-fileUpload>

Place the brackets:

<p-fileUpload mode="advanced" name="demo[]" [maxFileSize]="1000000" [multiple]="true" chooseLabel="Buscar arquivo" (onUpload)="onUpload($event)"></p-fileUpload>

After upgrading


Also published here.