Version 20.1 of Angular introduces a set of interesting improvements. We will analyze topics such as simplified code in templates and tests, new capabilities in managing HTTP requests, and image rendering.
Binary assignment operators
Angular 20.1 introduces a long-awaited feature: support for binary assignment operators directly in component templates. These are:
- +=, -=, *=, /=, %=
- **=, <<=, >>=, >>>=
- &=, ^=, |=
- &&=, ||=, ??=
Until now, if we wanted to update the value of a variable in the template (e.g., increment a counter), we had to do it using a dedicated method in the component:
<button (click)="increment()">+</button>
increment() {
this.counter += 1;
}
Since Angular 20.1, this can be written much more simply:
user = { name: '' };
<button (click)="user.name ??= 'Anonymous'">Set user name</button>
<button (click)="counter += 1">+</button>
Thanks to this:
- we have less code in the component class
- simple operations are visible directly in the template
Upgraded NgOptimizedImage
The NgOptimizedImage directive received a new decoding parameter, which allows better control over the way images are decoded. This enhances control over performance and image loading while remaining compliant with HTML standards.
<img ngSrc="..." decoding="async" />
This parameter has 3 options:
- sync – decodes the image immediately during content loading
Can be used when we want the image to display precisely at the moment of rendering
- async – asynchronous decoding
Ensures smooth interface because the image is decoded in the background, not blocking interactions
- auto – automatic
The browser chooses the best method itself
New parameters in HttpResource and HttpClient
Support for cache and priority
In Angular 20.1, HttpResource has been extended with additional options: cache and priority, which correspond to parameters known from the Fetch API.
Cache determines whether and how the browser can use its internal cache when making an HTTP request. Angular did not support this directly before — now we can specify it explicitly.
This is useful, for example, when we want to enforce fresh data from the API without relying on the browser’s buffer.
Possible values:
- default – The browser decides on its own
- no-store – No data will be saved or read from the cache
- reload – Always fetches from the network (ignores cache)
- force-cache – Uses the cache, even if it is stale
- only-if-cached – instructs the browser to make the request only if a matching response is found in the cache – that is, if such a request was already sent at least once. If not, the request will not be sent at all, and the browser will return an error.
httpResource({
getConfig: () => ({
url: '/api/products',
cache: 'reload'
})
});
Priority is a new, experimental parameter that allows specifying how important a given request is, which theoretically can affect how quickly it gets processed.
We can use it, for example, when we have many parallel fetches and want some of them to be handled earlier.
Available options:
- high – Highest priority (e.g., data for fast interface rendering)
- low – Low priority (e.g., preloads, secondary data)
- auto – Default browser behavior
httpResource({
getConfig: () => ({
url: '/api/metrics',
priority: 'low'
})
});
Not all browsers support this yet (Internet Explorer, Chrome < 103, Firefox < 132, Safari < 17.2, Edge < 103), but it gives Angular an open path for future optimizations.
Support for mode and redirect
Since Angular 20.1, we can now set the mode and redirect options in the HttpResource configuration, which allows precise control over how communication with the API occurs – especially with external services (e.g., APIs from other domains).
Mode determines how the browser should treat the request regarding cross-origin resource sharing (CORS) policy.
Available options:
- cors – Allows access to resources from another domain (if the server sets the appropriate CORS headers)
- same-origin – Allows requests only to the same domain
- no-cors – Allows requests to other domains, but without the ability to read the response (very limited)
httpResource({
getConfig: () => ({
url: 'https://api.domena-zewnetrzna.com/data',
mode: 'cors'
})
});
Redirect determines how the browser should behave with responses like 301, 302, 307, etc. This allows, for example, detecting automatic redirects.
Available values:
- follow – Automatically follows redirects (default behavior)
- error – Throws an error if a redirect is encountered
- manual – The browser does not follow redirects – the developer can handle them manually
httpResource({
getConfig: () => ({
url: '/api/old-endpoint',
redirect: 'error'
})
});
Extension with credentials
Credentials is an option that controls whether and when the browser includes cookies, authorization headers, and other session data in an HTTP request – especially for cross-domain requests.
This is important for:
- session authentication (e.g., via HTTP-only cookies)
- APIs requiring client identification
- cross-origin connections (e.g., to a backend on a different domain, port, or subdomain)
Available options:
- same-origin – Default option: only if frontend and backend are on the same domain
- include – Always sends cookies and credentials, including to another domain
- omit – Never sends cookies or authorization headers
this.http.get('/api/user', {
fetchOptions: {
credentials: 'include'
}
});
Support for keepalive
Angular adds support for the keepalive option to the experimental HttpResource API, which is based on the low-level fetch(). Thanks to this, requests can now be marked as:
{ keepalive: true }
which allows them to be sent even when the page is unloading (e.g., closing the tab, navigating to another page). Although this option is limited to small POST or GET requests without a body (up to 64KB), it will be useful, for example, for saving metrics or cleaning up sessions.
const resource = httpResource({
method: 'POST',
url: '/api/session/close',
keepalive: true,
});
Injection context in loadComponent() and loadChildren()
From now on, we can inject various dependencies, e.g., a service, before executing loadChildren() and loadComponent(), and operate on them without errors.
{
path: 'home',
loadComponent: () => {
const config = inject(MyFeatureConfig); // wcześniej błąd!
return import('./home.component').then(m => m.HomeComponent);
}
}
Bindings option in TestBed
Writing unit tests using TestBed will now become a bit easier, because the new change allows passing, for example, inputs or @HostBinding() directly when creating the test component.
Until now, to test a component, we had to manually set each input after creating the component.
it('old', () => {
const fixture = TestBed.createComponent(MyComponent);
fixture.componentInstance.title = 'Hello';
fixture.componentInstance.hostClass = 'highlighted';
fixture.detectChanges();
const el: HTMLElement = fixture.nativeElement;
expect(el.textContent).toContain('Hello');
expect(el.className).toBe('highlighted');
});
Now we will be able to do this more clearly:
it('new', () => {
const fixture = TestBed.createComponent(MyComponent, {
bindings: {
title: 'Hello',
class: 'highlighted',
}
});
const el: HTMLElement = fixture.nativeElement;
expect(el.textContent).toContain('Hello');
expect(el.className).toBe('highlighted');
});
In short – the new bindings option:
- allows passing inputs and host bindings immediately
- eliminates the need to write fixture.componentInstance.xxx = …
- makes tests more declarative
DestroyRef.destroyed
DestroyRef is a mechanism introduced earlier in Angular (since v16) that allows reacting to the lifecycle of an instance (e.g., component, service) without needing to implement ngOnDestroy().
The new destroyed property lets you check whether a given instance has already been destroyed or is still alive.
The example below shows a case when we have an open search with a set delay. When the search is closed for some reason (i.e., destroyed), the preformSearch() function will not be unnecessarily executed because the component no longer exists.
search.valueChanges.pipe(debounceTime(300)).subscribe(value => {
if (!this.destroyRef.destroyed) {
this.performSearch(value);
}
});
This way, we avoid errors like ExpressionChangedAfterItHasBeenCheckedError because we don’t operate on variables that have already been destroyed.
Summary
Version 20.1 of Angular is primarily another step toward more ergonomic and efficient application development. The changes are not large but significantly improve comfort and project stability.
Let’s also remember to update Node.js to at least version 20.19, 22.12, or 24.0, as the new Angular version requires exactly these.