Introduction to State Management

When you work with Angular for quite some time, you realize that you implement similar services repeatedly. Most likely, you end up with services with BehaviorSubjects to store some data, and you have components that subscribe to these subjects exposed as read-only Observables.

If you’re not at that point yet, I suggest you stop reading this article and get into the above architecture pattern first. This is a good reading about what I call “progressive state management,” where you get into state management little by little. Start there.

Once comfortable with the above, you might start thinking about NgRx, NgXs, or other state management libraries.

Why state management?

The main goal of a state management library is to define a single way to handle data in your application. Instead of having your application data in multiple different services, all your data will belong in a single, massive object called state. That state has to follow several rules and cannot be updated directly.

State management is all about rules and conventions created by the ancestor of all state management libraries: Redux. All modern libraries use similar concepts and vocabulary.

Here are the three core principles of Redux:

  • Single source of truth: The app’s state is stored in a single object tree within a single store. In Angular applications, that store is usually a service that can be injected anywhere we want.
  • The state is read-only: The only way to change the state is to emit an action, an object describing what happened and how we want to change our state.
  • State changes are made with pure functions called reducers. These functions take the previous state, an action, and return the next state. They return new state objects instead of mutating the previous state.

Visually, a typical Redux application works like this: A user interaction triggers an action that updates the global state using a reducer, and then components receive state updates to render the new data:

I know what you’re thinking at that point: That’s a lot of definitions and vocabulary. But this is what all state management libraries give us, so there’s no way around it. For instance, here are the links to these concepts in 3 different state management libraries:

  • NgRx: State (notice how that class extends BehaviorSubject) – ActionReducer.
  • NgXs: StateActionReducer (note that NgXs does not mention reducers directly and tries to simplify how they work, which is one of the reasons why it’s my state management library of choice)
  • Redux: StateActionReducer

We’ll dive into these topics in more detail with a series of posts in the next few days/weeks.

What is the Ivy engine?

You’ve probably heard about the Ivy engine if you’ve been working with Angular for some time. Ivy is the code name of Angular’s current compilation and rendering pipeline.

Before Ivy, Angular used a two-step process: Building a project would compile all Typescript code to Javascript. Then HTML templates would get compiled at runtime in the browser, using a second compiler called the View Engine. This was done for performance reasons in the past, but the toolchain and the framework evolved for much better performance using the Ivy Engine.

View Engine was deprecated in version 9 and removed from the framework in version 13. Ivy is the only option now and results in:

  • Faster builds and smaller build output. Some applications became up to 80% smaller/quicker when they started using Ivy – mostly because the View Engine compiler doesn’t need to be shipped to the browser anymore, but also because of the improved tree shaking.
  • Faster runtime experience – The application loads faster since components no longer need to be compiled in the browser.

If you’re stuck with an older version of Angular, you can look at this guide on upgrading Angular. If the guide doesn’t work, 99% of the time, the problem comes from your dependencies, and following these best practices to pick your dependencies would help.

Use Lighthouse to improve your Angular applications

As Angular developers, we tend to focus on component architecture, modules, TypeScript, and the best framework use. Most of the time, those things differ from what matters to end users.

End users usually want:

  • Performance – 60% of the web’s traffic happens on smartphones that don’t always have fast internet connections.
  • Accessibility – Is your website accessible to everyone? Did you check if color-blind people could see it correctly? Do you use alternate labels for images and buttons for screen readers that read the content to a blind user?

And, of course, if your website is supposed to be discoverable on the web, there’s search engine optimization (SEO).

The best way to know how you’re doing in all these categories (and more) is to use a Google Chrome browser built-in feature called Lighthouse. It’s a tab available in the dev tools:

Navigate to your web app (a public URL is needed), open Lighthouse in the dev tools, and click the “Analyze page load” button. Note that you can also simulate a mobile device to get a different report. You’ll get a report with scores in all these categories:

Clicking on any of the scores gives you a TODO list of possible improvements. You can expand every item to get more information about what to fix, how to do it, and why it’s important:

The nice thing about Lighthouse is that once you have improved your app, it takes just a few seconds to test your website again and see your scores increase.

ngx-mask for user input masking

HTML forms are evolving but still lacking a little in masking, which really means “suggesting the proper format to the user.

For instance, if I need to capture a US phone number in a specific format, I’d like to use a mask that looks like this:

We could use a placeholder, but the placeholder disappears as soon as we start typing, whereas a mask should remain visible and try to make the user input fit into that format. For instance:

This is where ngx-mask helps. It’s a third-party library that passes my acceptance criteria for dependencies. ngx-mask comes with directives and pipes to implement customizable masks.

To install it: npm install ngx-mask

You can find out how to configure the library here. Here is an example of the mask directive used for a US phone number:

And another one for a French phone number that doesn’t show the mask as we type but still enforces it:

The result looks like this:

Here is a link to my example on Stackblitz.

You can try many different demos on this website. The library supports regular expressions and lots of other options, such as a validation attribute to invalidate the form when the input is invalid:

And it’s possible to have multiple different acceptable formats on a mask, too:

Angular 16.2: NgComponentOutlet input bindings

Another feature of Angular 16.2 is using input bindings with component outlets. You might not be familiar with ngComponentOutlet in the first place, so let’s explain what the directive does.

Let’s consider the following template syntax:

The above code would load a dynamic component into the ng-container. Assigning a new component type to componentTypeExpression would display it in that container. Here’s a basic example that shows a HelloComponent in the component outlet:

This can be helpful if you need to use different types of components and don’t want to (or can’t) use the router to make it happen.

The new feature of Angular 16.2 allows us to pass input values to that component. Let’s assume that HelloComponent looks like this:

We can bind a value to the name input as follows:

And that does the trick! You can see an example in action on Stackblitz here. inputs is an object that can have as many keys as needed. In my example, there is only one: name.

What’s new in Angular 16.2? Lifecycle methods

Angular keeps evolving at a steady pace, and Angular 16.2 brought a couple of interesting features to the table.

The first feature is an addition to the Signals API that was part of a specific RFC for Signal-based components. The new feature consists of two functions:

afterNextRender

A function that allows registering a callback to be invoked the next time the application finishes rendering. It is similar to the ngAfterViewInit lifecycle method but for signal-based components.

The main use-case for this new lifecycle is to initialize a third-party, non-Angular-friendly library to hook itself on the DOM after Angular generates it:

afterRender

A function very similar to the afterNextRender, with the difference that instead of running the registered callback just once, it will run it after every single render, similar to ngAfterContentChecked. This could be used to read something from the DOM when it changes, such as the height of an element:

Angular Material Theme Generator

As we started covering Angular Material with a few different posts, today, I want to address one of the common objections to Angular Material: Sometimes, people don’t like the look and feel of the library and fear that it won’t match their business requirements.

While it is true that the default themes picked by the Material team aren’t for everyone, with a lot of bright pink, purple or flashy green, I have seen several companies customize Material to make it their own with great success.

Here are a few examples:

Bank of Montreal (BMO)

Air France

You can see that in some cases, it’s actually hard to recognize Material components in those apps. On the Air France website, the tabs and slide toggle are the most recognizable components.

How to generate my own custom theme?

One of the easiest way to see what’s possible is to use the (unofficial – though it comes from a team at Google) Material Theme Generator:

With the theme generator, you can:

  • Generate a theme from an image (such as a company logo!) and the tool will pick the colors and do the rest
  • Tweak all possible variables using color pickers and see the result on every component on the right-hand side.
  • Then export your customization as a CSS file using th “Download CSS” button.

Here I tried uploading a picture of mine from the Canyonlands of Utah last winter, and the resulting auto-generated theme is impressive – I didn’t touch any of the settings:

Migrating Angular Material to v15+

This is a guest post by Duncan Faulkner. You can read his first post on Angular Material here. This is the third and final post in this series.

Note: This post will cover upgrading to Angular Material version 15 from Angular Material 14—all changes affected version 15. Once the migration to Angular Material 15 is complete, migration to Angular Material 16 is straightforward.

The team at Angular Material has created a schema that will do all the heavy lifting when migrating to the new MDC format.

To upgrade your application to version 15

ng update @angular/material@15Code language: CSS (css)

This will automatically run the migration schema and move your project over to use the legacy version, ensuring that your application continues to run with minimal changes.

Once your project has upgraded, you can now run the migration tool. This will migrate from the legacy to the new MDC format.

ng generate @angular/material:mdc-migration

This will update your project, including TypeScript, styles, and templates, as much as possible. Where it cannot update your code, the migration tool will insert comments into your code for a developer to fix.

// TODO(mdc-migration):...Code language: JSON / JSON with Comments (json)

It’s also possible to migrate parts of your application; this can be a single component or a range of components, and the migration script will prompt the directory and components to migrate.

The migration to MDC is a welcoming and positive step. It improves accessibility, adheres to the Material Design Specification, and allows better adoption of future specification versions.

Legacy components

Currently, the Angular Material library includes all the legacy components. If you’re not able to upgrade to the latest version and want to continue to use these legacy components, you need to import the legacy module, for example:

import {
  MatLegacySliderModule,
} from '@angular/material/legacy-slider';

@Component({
  selector: 'slider-formatting-example',
  templateUrl: 'slider-formatting-example.html',
  styleUrls: ['slider-formatting-example.css'],
  standalone: true,
  imports: [MatLegacySliderModule],
})
export class SliderFormattingExample { 
    ...
}Code language: JavaScript (javascript)

Please note: The legacy versions are currently marked as deprecated and will be removed in version 17 of Angular Material.

Accessing route information with Angular

The Angular router comes with many interesting features, such as lazy-loading, guards, resolvers, or nested routes.

One of these features is the ability to access route parameters and route information within the component displayed by the router. This can be done by injecting the ActivatedRoute service in our component.

This ActivatedRoute contains a lot of information:

The most commonly used attributes are:

  • paramMap: An Observable of route parameters such as id in the route /product/:id/info
  • queryParamMap: An Observable of query parameters such as q in the route /product/search?q=test
  • data: An Observable of the data resolved by route resolvers such as hero in this example of a resolver.

snapshot gives us the current value of those same parameters instead of using Observables.

Another option appeared with Angular 16. We can bind router information to component inputs instead of using ActivatedRoute.

This works for the three categories of parameters covered earlier: Route params, query params, and resolved route data.

This means that this old and verbose approach:

can be replaced with the following concise and router-independent code:

The only config needed for this to work is the one line of code described in this article.