Angular 20.1 was released, and it came with a set of nice new features. Here are the main ones:
🧠 Signals DevTools Integration
The Angular DevTools works on a component level. So you have to select the component first, activate the SignalGraph, and then you can see all the signals with context information.
Supported are signal()
, computed()
, linkedSignal()
, and even effect()
.
This feature is still experimental. So we don't see the resource
as one unit, but as a collection of multiple signals.
You also have the option to name your signals via the debugName
property.
Example:
// Define a signal with a debug name for easier DevTools debugging const number = signal(1, { debugName: 'number', }); // Create a computed signal that doubles the `number` signal const double = computed(() => number() * 2, { debugName: 'doubleNumber', }); // Set up an effect to log the value of `double` whenever it changes effect(() => console.log(double()), { debugName: 'logger', });
🌐 Extension for httpResource
& HttpClient
The HttpClient
— and logically also the httpResource
, which uses the HttpClient
under the hood — received a bit of a power-up.
It now allows us to pass through properties that the native fetch
function exposes. These include support for timeouts, caching, redirect handling, and more.
Example:
// Define an httpResource with native fetch options passed through httpResource(() => ({ url: `/holiday/1/quiz`, priority: 'high', redirect: 'follow', cache: 'no-cache', credentials: 'same-origin', mode: 'no-cors', })); // Equivalent call using HttpClient directly with fetch-like options this.httpClient.get('/holiday/1/quiz', { priority: 'high', redirect: 'follow', cache: 'no-cache', credentials: 'same-origin', mode: 'no-cors', });
🧪 Tests with Bindings
Whenever we wanted to test a component with property binding, we had to come up with a wrapper component to simulate a parent component, or execute ComponentRef.setInput()
.
Now, the TestBed.createComponent()
method has a second parameter for bindings.
It doesn’t just support property binding but also event binding and two-way binding.
Given a component with two property bindings timeLeft
and status
:
export class QuizStatusComponent { timeLeft = input.required<number>(); status = input.required<{ correct: number; incorrect: number }>(); }
The "old way" was to rely on componentRef
:
it('should show the time left', async () => { TestBed.configureTestingModule({ providers: [provideZonelessChangeDetection()], }).createComponent(QuizStatusComponent); // Setting the inputs fixture.componentRef.setInput('timeLeft', 10); fixture.componentRef.setInput('status', { correct: 0, incorrect: 0 }); const timeLeft = await screen.findByLabelText('Time remaining'); expect(timeLeft.textContent).toContain('Time Left: 10 seconds'); });
In Angular 20.1, we can do that much more easily via createComponent
:
it('should show the time left', async () => { TestBed.configureTestingModule({ providers: [provideZonelessChangeDetection()], }).createComponent(QuizStatusComponent, { bindings: [ inputBinding('timeLeft', () => 10), inputBinding('status', () => ({ correct: 0, incorrect: 0 })), ], }); const timeLeft = await screen.findByLabelText('Time remaining'); expect(timeLeft.textContent).toContain('Time Left: 10 seconds'); });
🤖 MCP for Angular CLI
For AI-assisted development — which hopefully we’re all doing at this point — the Angular CLI provides an MCP server.
You need to register it in your IDE, and from that moment on, you can generate Angular code or ask Angular-specific questions right from your editor.
Depending on the IDE, in Cursor the registration for the MCP server would look like this:
mcp.json
{ "mcpServers": { "angular-cli": { "command": "npx", "args": ["@angular/cli", "mcp"] } } }
🧩 Miscellaneous
Sometimes we need to change or assign values to properties when a certain event happens. If it’s just a one-liner, we can now do that directly in the template — thanks to a syntax extension that supports different types of assignment operators.
Given that signals are the way forward and we should avoid logic in templates, this is likely an edge case.
Last but not least, it’s now possible to import image files in TypeScript, which can, for example, be encoded in base64 and reused in templates.
Before:
@Component({ template: ` <img alt="Eternal" src="assets/logo.png" /> ` }) export class Header {}
After:
import logo from '../../../assets/logo.png' with { loader: 'base64' }; @Component({ template: ` <img alt="Eternal" [src]="logo" /> `, }) export class Header { protected readonly logo = `data:image/png;base64,${logo}`; }
Yes, the older version is shorter — but the new version embeds the image directly into the component, so it doesn’t need to be loaded separately.
Top comments (0)