Skip to content

Best Practices

Stefan Saring edited this page May 27, 2024 · 4 revisions

In Angular there is often more than one option to achieve a specific goal. This page lists several topics, where multiple solutions are available and provides best practices and hints for choosing the best suited option.

OnPush vs. Default change detection

Change Detection in Angular

When you change any of your models, Angular detects the changes and immediately updates the views. This is change detection in Angular. The purpose of this mechanism is to make sure the underlying views are always in sync with their corresponding models. This core feature of Angular is what makes the framework tick and is partly the reason why Angular is a neat choice for developing modern web apps.

Change Detectors

All Angular apps are made up of a hierarchical tree of components. At runtime, Angular creates a separate change detector class for every component in the tree, which then eventually forms a hierarchy of change detectors similar to the hierarchy tree of components.

Whenever change detection is triggered, Angular walks down this tree of change detectors to determine if any of them have reported changes.

OnPush strategy

By default, Angular uses the "Default" change detection strategy, where it checks for changes in all components and their templates on every change detection cycle. This can lead to unnecessary checks and updates, impacting performance in larger applications.

The "OnPush" strategy provides a way to mitigate this by making components more selective abou when they should be checked for changes. Updates will be performed only when inputs or properties change.

When to use which?

The "OnPush" change detection strategy is a good choice in case you have components that are not constantly changing. This includes components that:

  • Display static data
  • Only receive data from their parent components
  • Do not perform any calculations or transformations on their data

The "Default" change detection is suitable in simple applications where update performance does not matter that much. More complex applications should favor the OnPush strategy, as mixing of strategies will add additional complexity and error sources.

Code example

Documentation

Signals vs. Observables

Angular 16 has introduced a feature called “Signals” which allows defining reactive values and expressing dependencies between them.

Signals

Signal is a wrapper around a value that can notify interested consumers when that value changes. Signals can contain any value, from simple primitives to complex data structures.

A signal’s value is always read through a getter function, which allows Angular to track where the signal is used. Signals may be either writable or read-only.

It avoids the need for manual subscription management or usage of async pipe and ensures the UI remains in sync with the profile data.

Observables

In Angular, Observables are part of the Reactive Extensions for JavaScript (RxJS) library. Observables are a powerful feature used extensively in reactive programming to handle asynchronous operations and data streams. Observables provide a way to subscribe to and receive notifications when new data or events are emitted, enabling you to react to changes.

When to use Observables?

The Styleguide example shows that using an observable requires to manually subscribe to it, handle the output, and unsubscribe when done. The usage of observables can be simplified by using async pipes (|), if side-effect are not needed during subscription.

Subscribing to an observable is like listening to new events that filter down to the consumer. There can be new events, but there could also be an error, or an indication to say that the observable has finished.

The stream of events can be managed by piping events through a pipe. These pipes can indeed become extremely complex, but they can be very elegant in managing an asynchronous stream of data over time.

For this reason, you’ll usually see observables in services within your Angular application.

When to use Signals?

Signals can be used in any part of the application for simplifying the handling of objects to be observed. E.g. they can be used in the UI layer, async pipes in templates are then not needed anymore. Signal can be updated via the set method, or make a change to an existing value via update.

Since you’re setting or updating the signal each time, there isn’t really a built-in error state, like with an observable. However, you can define your own error state and update a signal accordingly.

Also, you can use computed signals to compose new signals based on existing signals. Changes will propagate through these computed signals when dependent signals are updated.

If your template is being updated reactively in your code, it would make sense to use a Signal instead of an Observable.

Code example

Documentation

Lazy loading vs. Default loading

Default loading

Since Angular creates a SPA (Single Page Application), all of its components are loaded at once. Default loading or also called Eager loading is the default loading strategy in Angular. In this approach, all modules are loaded when the application starts, regardless of whether they are immediately needed or not. This can lead to larger initial bundle sizes, which may impact the application’s loading time.

Lazy loading

Angular provides a powerful feature called Lazy Loading which allows developers to load modules and components on-demand. Lazy loading is technique that allow user to defer loading of certain parts of an Angular application. Parts mean certain modules in the application. These modules are loaded dynamically when a user navigates to a route associated with that module, it will reduce the initial load time as well.

Lazy loading is essential for optimizing the performance and user experience of an application. Lazy loading allows the application to load only the essential components and modules needed for the initial view. Other parts are loaded later on when the user navigates to them through the application.

When to use Default / Eager Loading?

  • Case 1: Small size applications. In this case, it’s not expensive to load all modules before the application starts, and the application will be faster and more responsive to process requests.

  • Case 2: Core modules and feature modules that are required to start the application.

    • These modules could contain components of the initial page, interceptors (for authentication, authorization, and error handling, etc.), error response components, top-level routing, and localization, etc.
    • To make the application function properly, these modules have to be eagerly loaded, despite the application size.

When to use Lazy Loading?

The scenario of applying Lazy Loading is relatively simple and straightforward. In a big-size web application, we can lazily load all other modules that are not required when the application starts.

Documentation

NgZone

In Angular, NgZone is a service provided by the Angular framework that helps manage and control the execution of asynchronous tasks and change detection.

It is responsible for triggering change detection and updating the view when changes occur. The primary purpose of NgZone is to handle and optimize the execution of code that runs outside of Angular’s zone, such as events from third-party libraries or asynchronous operations like timers, AJAX requests, or WebSockets.

By default, Angular runs in a zone called the Angular zone. When code executes within this zone, Angular’s change detection mechanism is triggered automatically, and the view is updated accordingly.

NgZone provides a way to explicitly run code inside or outside of the Angular zone. It offers two methods for executing code: run() and runOutsideAngular().

  • run()
    • The run() method executes the provided function inside the Angular zone.
    • This ensures that any changes triggered by the function will be detected and updated in the view.
  • runOutsideAngular()
    • The runOutsideAngular() method allows to execute code outside of the Angular zone.
    • This is useful for running code that doesn’t require Angular’s change detection or when optimizing performance for tasks that don’t affect the UI.

When to use it?

NgZone shall be used to control and optimize the execution of code inside and outside the Angular zone, ensuring efficient change detection and synchronization between the application state and the view.

Long running operations which does not need to trigger the change detection should always be executed outside the Angular zone. This makes the application more performant with eliminating the frequent change detection triggering.

Documentation