Angular 14

Featured image

Angular 14 Migration

How are you doing? We are back with a new angular migration! Over here I share the schedule of releases and official versions.

Today’s post is dedicated to the experiences during a specific migration from a previous Angular 13 to Angular 14, I share news, tips, bugs and their respective fixes to renew and take it out with fries!

Memes About ‘The Office’ Revival Rumors Are The Internet At Its Best](https://imgix.bustle.com/uploads/image/2017/12/19/1b3f939e-752b-4d8b-80ba-b2384790f8ad-michael-scott-office.jpg?w=1200&h=630&fit=crop&crop=faces&fm=jpg)

What’s New

Let’s review some nice new features that this version brings.

Standalone Components

Before you jump for joy and start coding standalone components, I must warn you that in ng14 this feature is in DEVELOPER PREVIEW mode. Which means that, they can be explored and experimented but in dev mode, they are NOT in their stable point yet. So it is very likely that its API will suffer changes in the next versions.

Now, back to the topic, the goal of the Angular standalone components is to speed up the creation of Angular applications reducing the need for NgModules. Complete and official info on standalone components by here

Note: Angular classes marked as standalone, do not need to be declared in an NgModule (in fact the Angular compiler reports an error if you try it).

Example: Create Standalone component

Standalone components can specify their dependencies directly, instead of getting them through NgModule. For example, if PhotoGalleryComponent is a standalone component, you can directly import another standalone component ImageGridComponent:

@Component({
  standalone: true,
  selector: "photo-gallery",
  imports: [ImageGridComponent],
  template: ` ... <image-grid [images]="imageList"></image-grid> `,
})
export class PhotoGalleryComponent {
  // component logic
}

Example: Loading Standalone component (Lazy loading)

Also, any path can load a standalone component by loadComponent (instead of loading a module):

export const ROUTES: Route[] = [
  {
    path: "admin",
    loadComponent: () =>
      import("./admin/panel.component").then((mod) => mod.AdminPanelComponent),
  },
  // ...
];

But well, I don’t want to tempt you with this feature, later on we can take the leap of faith and start experimenting with it when it is in a more stable stage.

Change detection

In Angular, the change detection is very optimized and very effective, but it can cause slowdowns if the app runs this detection too often.

In this guide here, we can see ways to control and optimize the change detection mechanism by skipping parts of our app and running change detection only when it is really necessary.

Diagnostics

Many times it can happen that, part of what we code is technically valid, as much for the compiler as for the runtime, HOWEVER, it does not mean that we are free of evil, of bugs, even if technically it is “ok”. That’s why the Angular compiler includes “extended diagnostics” that identifies many of these cases, in order to warn us about possible problems so we can comply with best practices.

These are the ones currently supported:

To go into more details and interesting readings of each one, I share the official docu here

Angular CLI

Some Angular CLI enhancements worth noting: ng completion For typos on command lines, we have the new ng completion in v14 which introduces real-time autocompletion.

Angular DevTools offline and in Firefox

I leave for here the Firefox DevTools

Migration guide

Well now, let’s get to work. To start we go to the official guide here. In this case, coming from version 13, we don’t need to apply changes to our code BEFORE running the command in order to migrate. Therefore, we can start:

ng update @angular/core@14 @angular/cli@14

Most of the required changes we apply with the command. The rest of the possible changes we need to make will, as always, depend on the project we are working on and this guide may cover several of those cases.

Changes

For this migration we must take into account some fundamental changes:

Problems during migration

As in any migration, there is some uncertainty as to what problems we may encounter along the way. In general terms, if you come from a relatively new version such as ng13, your tests are in Jasmine, you run them with Karma and nothing strange, no strange dependencies or anything crazy, life will smile on you and you will migrate without major problems other than the odd typing issue or issue that is simple to solve. Lately, the bugs are more explicit and clear, making it easier and faster for us to fix them.

There could be compatibility problems with some dependencies and external libraries as well, for this we would have to check if it is possible to update them or analyze different alternatives to adjust those dependencies.

Let’s see some common issues and problems.

Jest

If you are using Jest for unit tests, I recommend the following versions that work well with ng14:

    "jest": "^28.1.3",
    "jest-preset-angular": "^12.2.3",
    "@types/jest": "^28.1.1",
    "ts-jest": "^28.0.8",

Error

In case you encounter this error:

    ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,jest){import * as i0 from '@angular/core';
                                                                                                                                               ^^^^^^
        SyntaxError: Cannot use import statement outside a module

Fix

Es necesario que ajustes tu jest.config, con el transformIgnorePatterns y las dependencias en cuestión:

// jest.config.js
module.exports = {
  // ...other options
  transformIgnorePatterns: ["node_modules/(?!@angular|@ngrx)"],
};

Theory and explanation

This happens because by default, Jest does NOT transform node_modules, because they should be valid JavaScript files. However, the authors of the libraries assume that you will compile their sources. So in these cases, you have to tell Jest this explicitly. The above code means that @angular, @ngrx will be transformed, even though they are node_modules.

Now, if the dependency causing the problem is a subdependency of a node_modules package or a module designed to be used with nodeJS, a custom resolver might be needed to fix the problem. Since in these cases, the transformIgnorePatterns rule, it is not enough to add those dependencies to our whitelist.

Error

So, if we encounter problems between subdependencies of any package, we will encounter an error like this:

    node_modules\@angular\fire\node_modules\@firebase\firestore\dist\index.esm2017.js:12705
                        function (t, e) {
                        ^^^^^^^^

        SyntaxError: Function statements require a function name

Fix

To solve it, we need to build a custom resolver for jest, as I mentioned above, and this will be useful for some dependencies and subdependencies, such as: Firebase, Ngrx, Rxjs, etc.

The resolver would look like this:

// jest.resolver.js
module.exports = (path, options) => {
  // Call the defaultResolver, so we leverage its cache, error handling, etc.
  return options.defaultResolver(path, {
    ...options,
    // Use packageFilter to process parsed `package.json` before the resolution (see https://www.npmjs.com/package/resolve#resolveid-opts-cb)
    packageFilter: (pkg) => {
      const pkgNamesToTarget = new Set([
        "rxjs",
        "@firebase/auth",
        "@firebase/storage",
        "@firebase/functions",
        "@firebase/database",
        "@firebase/auth-compat",
        "@firebase/database-compat",
        "@firebase/app-compat",
        "@firebase/firestore",
        "@firebase/firestore-compat",
        "@firebase/messaging",
        "@firebase/util",
        "firebase",
        "ngrx",
      ]);

      if (pkgNamesToTarget.has(pkg.name)) {
        // console.log('>>>', pkg.name)
        delete pkg["exports"];
        delete pkg["module"];
      }

      return pkg;
    },
  });
};

For other common issues and troubleshooting of jest-preset-angular, I share some solutions by here

NX

Now yes, NX is another story and another world in migration. As a guide to migrate from previous versions of NX to one compatible with Angular 14, I share 2 aspects:

NX Storybook

With Storybook in a repo with NX, you will probably find that nothing works, both for serve and for build of the storybook of our app. Most likely you were using the NX builders (@nrwl):

    @nrwl/storybook:storybook
    @nrwl/storybook:build

From now on, we are going to use pure storybook builders, that is, the same ones we use to serve and build storybooks in environments, projects and repositories WITHOUT NX:

@storybook/angular:start-storybook
@storybook/angular:build-storybook

I leave you more details as an additional guide to the thread here! and also just in case, the official Storybook docu for Angular here

Conclusion

Migrating from Angular 13 to Angular 14 could become complex in some situations but it has its benefits, among the most important ones:

Finally, I share references and complementary readings by here

Happy coding!

Beer!