Analyzing your bundle size

We covered build budgets and how they can help keep your application performant by detecting when a new dependency dramatically increases the size of your build output.

If you want to look at your build bundle and determine which dependency/module is the biggest, there’s another tool at your disposal: The Webpack Bundle Analyzer.

To install it, use the following npm command:

npm install --save-dev webpack-bundle-analyzer

Then, run your Angular build with the option that generates build statistics:

ng build --stats-json

Finally, run the Webpack Bundle Analyzer to read those build stats and give you a visual output of that bundle:

npx webpack-bundle-analyzer dist/stats.json

This command opens a new tab in your browser with the following user interface. Each rectangle represents a Javascript module. The bigger the rectangle, the bigger the module.

In the above example, we can see that the application code (main.js – in green color) is a lot smaller than the application dependencies.

For instance, we could improve this project by removing polyfills.js, as these polyfills were included to maintain compatibility with the now-retired Internet Explorer, and they take more space than our application code!

Using the Angular Material Slider

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

The mat-slider was recently rewritten in Angular 15 as part of a refactoring towards MDC (Material Design Components for the Web). The most notable change to the mat-slider is the element now requires up to two input elements, instead of just a mat-slider element, this allows us to use the slider as either a single slider (with one input) or a range (with two inputs).

The original mat-slider looked like this in versions prior to Angular Material 15:

<!-- original -->
<mat-slider></mat-slider>Code language: HTML, XML (xml)

And in versions after 15:

<!-- single slider -->
<mat-slider>
  <input matSliderThumb />
</mat-slider>

<!-- range slider -->
<mat-slider>
  <input matSliderStartThumb />
  <input matSliderEndThumb />
</mat-slider>Code language: HTML, XML (xml)

Slider usage options

It’s also possible to set the range slider to have a min/max or both, for example:

<!-- range slider -->
<mat-slider [min]="30" [max]="75">
  <input matSliderStartThumb />
  <input matSliderEndThumb />
</mat-slider>Code language: HTML, XML (xml)

If min is not supplied then zero is assumed, likewise if max is not supplied then one hundred is assumed. If step is supplied then maxis required otherwise the slider will not be able to calculate the step division.

The thumbLabel directive has now been replaced with a new discrete attribute, this controls whether the value indicator tool-tip is shown when the slider is dragged.

<!-- original -->
<mat-slider thumbLabel></mat-slider>

<!-- replaced with -->
<mat-slider discrete>
  <input matSliderThumb />
</mat-slider>Code language: HTML, XML (xml)

To show the tick-marks on the slider add the showTickMarks attribute.

<mat-slider showTickMarks>
  <input matSliderThumb />
</mat-slider>Code language: HTML, XML (xml)

For now the tickInterval property has been removed from the API, though this is being reviewed and could be reintroduced in the future. If we want to define the interval of the tick marks we use the step property and the tick marks will match the step:

<mat-slider discrete showTickMarks step="10">
  <input matSliderThumb />
</mat-slider>Code language: HTML, XML (xml)

The displayValue property has also been removed in favour of the new displayWith this property controls the text value of the indicator, we need to provide a function for this property where we can manipulate the value to be displayed. We use the min property to set the lowest value, the max property to the highest value we want the slider to be an set the step property to what we want the increment to be.

<mat-slider min="0" max="100000" step="1000" [displayWith]="updateLabelWithFn">
    <input matSliderThumb />
</mat-slider>Code language: HTML, XML (xml)
export class MySliderComponent {
  updateLabelWithFn(value: number): string {
    return value >= 1000 ? Math.round(value / 1000) + 'k' : `${value}`;
  }
}Code language: JavaScript (javascript)

The updateLabelWithFn function takes the value from the slider and [in this instance] if the value is greater than or equal to 1000 then it will round the number and divide it by 1000 and concatenate the letter k to the end, this helps to keep the text small and in the thumbLabel.

The valueText property has also been removed, we now have two options we can use the input’s aria-label-valueText or use the displayWith property.

<!--before Angular 15 -->
<mat-slider [valueText]="someTextValue"></mat-slider>

<!--after Angular 15 -->
<mat-slider>
  <input [attr.aria-valueText]="someTextValue" matSliderThumb />
</mat-slider>

<!--after Angular 15 -->
<mat-slider [displayWith]="displaySomeTextWithFn">
  <input matSliderThumb />
</mat-slider>Code language: HTML, XML (xml)

With the rewrite the API of the slider has also changed and has introduced two new components the MatSliderThumb and MatSliderRangeThumb and provide the following properties:

  • @Input() value: number
  • @Output() valueChange: EventEmitter
  • @Output() dragEnd: EventEmitter
  • @Output() dragStart: EventEmitter
  • @Input() percentage: number

And the following methods:

  • blur
  • focus

There are two notable absences from the mat-slider and these are:

  • invert – this reversed the start and end of the slider
  • vertical – this rotated the slider 90°, you could also invert a vertical slider

These have been removed as they are not part of the Material Design Specification for the Web (MDC).

Changing the color of a slider

Like all Angular Material components we can change the color of a mat-slider using the color property.

<mat-slider [color]="primary">
    <input matThumbSlider>
</mat-slider>Code language: HTML, XML (xml)

In our next post, we’ll see how to migrate from Angular Material 14 to 15+.

Asynchronous form validation with Angular

We’ve already covered how to write custom form data validators with Angular. We also covered how to display custom validation feedback to the user. Today, let’s take a look at how to perform asynchronous validation of our form data, which is useful when we need to make an HTTP request to a server to validate user input.

The approach for asynchronous validation is very similar to what we do with regular validation. The only difference is that instead of returning ValidationErrors, we return an Observable or a Promise of such ValidationErrors:

A ValidationErrors object is any keys and values you want to return. I like using keys that are the name of the form input throwing the error and the value being the error message I want to display on the screen. For instance, this is one of my typical ValidationErrors objects:

If you want to dig deeper, here is a link to a tutorial showcasing an example of an async validator. It’s also worth noting that asynchronous validators introduce a new validation state besides VALID and INVALID, which is PENDING. You can use that validation state to display a spinner or temporarily disable a form input while the asynchronous validation occurs.

ngOnDestroy lifecycle hook

After covering ngOnChanges and ngOnInit earlier, let’s talk about ngOnDestroy. The ngOnDestroy lifecycle hook is called when an Angular component/directive/pipe is destroyed. I’ll refer to components only in this post, but everything you read also applies to directives and pipes.

This can happen when the component is removed from the DOM, or the application is destroyed.

Most of the time, a component gets destroyed when it’s removed from the DOM as a result of:

  • A ngIf directive condition becomes false, thus hiding the element from the screen.
  • The router navigates to a different screen, thus removing the component from the DOM.

The ngOnDestroy hook is a good place to do cleanup tasks, such as unsubscribing from Observables (though there are better options to automatically unsubscribe your Observables), closing sockets, or canceling setInterval and setTimeout tasks (though you could use RxJs timer for that):

Getting Started with Angular Material

This is a guest post by Duncan Faulkner. He is an Angular developer for a fintech company based in the UK. A software engineer with over twenty years of experience, Duncan has been working with Angular since the early betas, written blog posts for ng-conf and several short books on Angular Material. He’s also the maintainer for the @ngbracket/ngx-layout (formerly @angular/flex-layout) open-source project.

You can follow him on Twitter, LinkedIn, Medium and YouTube by using this bio link.

Angular Material is a component library for Angular projects, this library comprises of a number of User Interface components (UI) and a Component Development Kit (CDK). In this series of posts we’ll use Angular Material version 16, there haven’t any major changes to this version of the library.

Some of the components in this library are:

  • buttons
  • inputs
  • cards
  • date picker
  • progress bar
  • grids
  • toolbar
  • and many more:

Adding Angular Material to an existing Angular application is quite straight forward, navigate to your Angular project in a terminal window and type:

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

This will install Angular Material, the CDK and the Angular Animations, during this installation process we are asked to make a few choices.

  • The first one will show the version of Angular Material that will be installed and whether to proceed or not.
  • Next, choose either one of the pre-built themes or create a custom theme.
    • The available pre-built themes are:
      • deeppurple-amber, Palette (Primary, Accent, Warn) – deep-purple, amber, red.
      • indigo-pink, Palette (Primary, Accent, Warn) – indigo, pink, red.
      • pink-bluegrey, Palette (Primary, Accent, Warn) – pink, blue-grey, red.
      • purple-green, Palette (Primary, Accent, Warn) – purple, green, red.
    • Custom – I will discuss this in a future post.
  • Next, include Angular Material typography styles in our project.
    • This will add the mat-typography CSS class to the body tag: <body class="mat-typography"> <app-root></app-root> </body>
  • Finally, include browser animations in our project.
    • Declining this will disable most of the animations of Angular Material.

Now the installation is complete, there have been a few changes made to our project.

  • The following two dependencies were added to the package.json file."dependencies" : { "@angular/cdk": "^16.2.1" "@angular/material" : "^16.2.1" }
  • The Roboto font was added to the index.html file: <link rel="preconnect" href="https://fonts.gstatic.com"> <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
  • The Material Design Icon font was also be added to the index.html file. <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">

There are also some changes to global CSS styles:

  • Removed the margins from the body tag
  • Changed the height : 100% on html and body tag.
  • Made the Roboto font as the applications default.

Now we have Angular Material installed, let’s try out an Angular Material component in our application. In the app.component.ts file add the following.

import { Component } from '@angular/core';
import { MatSliderModule } from '@angular/material/slider';

@Component({
  selector: 'app-root',
  templateUrl: 'app.component.html',
  styleUrls: ['app.component.scss'],
  standalone: true,
  imports: [MatSliderModule],
})
export class AppComponent {}
Code language: JavaScript (javascript)

And in the app.component.html file add the following line.

<mat-slider>
  <input matSliderThumb />
</mat-slider>
Code language: HTML, XML (xml)

Here we’ll just add a simple mat-slider to the page to test everything is working.

If we now run the application in the browser, we should now see a mat-slider on the page.

Coming up in part two of this series we’ll dig deeper into the mat-slider and the recent changes from Angular Material 15 as Angular Material 16 was mainly minor changes and bug fixes.

What you need to know about ngModules

Angular modules (ngModules) are a source of confusion for Angular developers. This short guide will clarify what they are and how to think about ngModules in general.

Why ngModules?

Angular uses a Typescript compiler and a template compiler that turns our HTML templates into Javascript instructions to render the DOM in a browser.

The Typescript compiler knows how to compile our Typescript code (all our classes) because all dependencies have to be imported in these Typescript files:

Compiling the HTML template for the above component is a different task because *ngIf is not imported anywhere in our Typescript code. Yet, the Angular compiler must know about all directive/component/pipe dependencies of a component’s template to turn all that HTML into DOM instructions.

The solution to that problem was the introduction of ngModules. ngModules expose features meant to be used by the template compiler and does so in a “global” way, in the sense that importing an ngModule once is enough to enable all its code for all components within that module.

That’s why we rarely think twice about using ngFor or ngIf or any pipe: They are all part of CommonModule, which is automatically imported into the AppModule by default:

Do I need to create ngModules in my app?

Most likely, no. The Angular team introduced standalone components as an alternative to ngModules. A standalone component does not need such modules because all its dependencies are listed in the Typescript code itself:

There were only two reasons why you’d need to create your own ngModules in the past:

That’s it. Both of these problems are solved by standalone components, which can be lazily loaded and already bring all their dependencies along, so no ngModule is needed.

What about core, shared, and feature modules?

Those were parts of guidelines created for the sake of consistency within Angular applications. But you don’t need these modules for your application to work. You can still organize your code neatly in folders and sub-folders and not use ngModules. You can even have tidy, short import statements without having ngModules.

Usually, the more ngModules you create, the more work and problems you’re likely to encounter (circular dependencies, anyone?), which is why the Angular team introduced standalone components and already migrated all its internal directives and pipes to standalone. In the long run, ngModules will likely disappear.

All ng-conf 2023 videos available for free

I talked about ng-conf 2023 quite a bit back in June and just wanted to let you know that all videos from the conference are now available online for FREE. All you have to do is enter your email address here.

Here are my top 5 recommendations from the conference:

Enjoy! The first 4 talks are about 20 minutes long, and the last one is 35 minutes long.

Using setters with @Input()

Following our theme of lifecycle methods with ngOnChanges and ngOnInit, I want to give you another interesting trick to be notified when an @Input value changes.

We’re already familiar with that syntax:

But what if instead of applying @Input on a class property, we used it on a setter:

The input works the same as before, with the advantage of running several instructions when a new value is set. All of that without using ngOnChanges. This approach is practical if you have several side-effects to trigger depending on which input changes, which would be tedious to handle with multiple conditionals:

The setter approach brings more clarity and leaves less room for mistakes:

ngOnInit lifecycle hook

A few days ago, we talked about the ngOnChanges lifecycle hook. We saw that every time an @Input() changes, Angular will trigger ngOnChanges. ngOnInit is similar but runs only once; after all @Input() receive their initial value. The official documentation puts it like this:

The above definition might surprise you because there is a widespread belief that the purpose of ngOnInit is to run all initialization tasks, even though the class constructor runs before ngOnInit, as explained a few months ago in this entry: ngOnInit vs. constructor in Angular.

As a result, using ngOnInit is only 100% justified when you want to run some code that needs the value(s) of @Input() properties of your component/directive. In other scenarios, you can use a constructor to run your initialization code sooner.