Following the release of my Master Angular 17 Study guide, Angular shipped two smaller updates: 17.1 and 17.2.
🎯Changes and new features
This write-up highlights the key modifications and additions, along with links to tutorials for understanding these new Angular capabilities:
- Signal-based model inputs
- Signal-based view queries and component queries
-
ngOptimizedImage: Automatic placeholders -
ngOptimizedImage: Netlify image loader support - Angular CLI:
clearScreenoption support - Angular CLI:
defineoption for declaring global identifiers
For enhanced code syntax highlighting, the same content is hosted on dev.to.
📌Model signal inputs
PR: Initial implementation of model inputs
Model inputs are a feature introduced in Angular 17.2. They are built on writable signals and create a pair of inputs and outputs, facilitating two-way binding. In the setup shown below, the signals in both components hold matching values, and pressing either button increments that value:
@Component({
selector: 'app-counter',
standalone: true,
template: `<button (click)="increase()">Counter's button: {{ value() }}</button>`,
})
export class CounterComponent {
value = model.required<number>();
increase() {
this.value.update((x) => x + 1);
}
}
@Component({
selector: 'app-wrapper',
standalone: true,
imports: [CounterComponent],
template: `<app-counter [(value)]="count" />
<button (click)="increase()">Wrapper's button: {{ count() }}</button>`
})
export class WrapperComponent {
count = signal(0);
increase() {
this.count.update((x) => x + 1);
}
}
The 'banana in the box' syntax [(ngModel)] allows two-way data binding to connect an input element's value directly with a writable signal.
@Component({
selector: 'app-root',
standalone: true,
imports: [
FormsModule,
],
template: `
<textarea
[(ngModel)]="promptValue"
></textarea>`
})
export class AppComponent {
promptValue = signal('');
}
📌View queries and component queries as signals
PR: feat(core): expose queries as signals
Thanks to this feature, template elements can now be queried as signals. The newly introduced viewChild(), viewChildren(), contentChild(), and contentChildren() functions each return a Signal, serving as signal-based counterparts to the @viewChild, @viewChildren, @contentChild, and @contentChildren decorators.
@Component({
selector: 'app-vc-query-as-signal',
standalone: true,
template: `
<button (click)="show()">Show</button>
@if(visible()) {
<div #id1>Hi!</div>
}`,
})
class VcQueryAsSignalComponent {
visible = signal(false);
divEl = viewChild<ElementRef<HTMLDivElement>>('id1'); // 👈
effectRef = effect(() => {
console.log(this.divEl());
});
show() {
this.visible.set(true);
}
}
// First message on the console: undefined
// The user clicks on the button
// Second message on the console: _ElementRef {nativeElement: div}
📌ngOptimizedImage: Automatic placeholders, Netlify image loader support
Official docs: Automatic placeholders
PR: feat(common): add Netlify image loader
PR: feat(common): add placeholder to NgOptimizedImage
Whenever an image CDN is in use, NgOptimizedImage gains the ability to show a low-resolution placeholder on its own.
On top of that, the Angular team introduced the provideNetlifyLoader preconfigured loader, which enables support for the Netlify image CDN.
@Component({
selector: 'app-image',
standalone: true,
imports: [NgOptimizedImage],
template: `
<p>Responsive image:</p>
<!-- 30 x 30 url encoded image as a placeholder 👇 -->
<img ngSrc="assets/lamp.jpeg" style="max-width: 1024px" [placeholder]="data:@file/jpeg;base64,..." />
`,
})
export class ImageComponent {
}
// app.config.ts
export const appConfig: ApplicationConfig = {
// provider for the Netlify image CDN 👇
providers: [provideNetlifyLoader('https://yoursite.netlify.app/')],
};
📌Angular CLI: clearScreen option support
PR: a957ede build: update angular
Prior to each rebuild, Angular is capable of wiping the terminal. To activate this behavior, configure angular.json with the clearScreen builder option assigned a value of true—its default state is false:
// angular.json
{
"projects": {
"ng172": {
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:application",
"options": {
// 👇 clear the screen before each re-build
"clearScreen": true,
// ...
📌Angular CLI: 'define' option for declaring global identifiers
PR: feat(@angular-devkit/build-angular): add define build option to application builder
The define option, now part of the application builder, lets you set up global identifiers. Since these identifiers are configured in angular.json rather than through a .ts file, you can still give them TypeScript types by adding a declare const inside src/types.d.ts. Down the road, these identifiers might serve as a substitute for environment files.
@Component({
template: `
Text: {{ CONSTANT_IN_ANGULAR_JSON.text }},
Number:{{ CONSTANT_IN_ANGULAR_JSON.number }}`,
})
export class GlobalIdentifierComponent {
CONSTANT_IN_ANGULAR_JSON = CONSTANT_IN_ANGULAR_JSON;
}
// angular.json
{
"projects": {
"ng172": {
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:application",
"options": {
"define": {
// the value must have a valid JSON syntax 👇
"CONSTANT_IN_ANGULAR_JSON": "{ 'text': 'This constant is defined in angular.json', 'number': 1 }"
},
// ...
// src/types.d.ts
declare const CONSTANT_IN_ANGULAR_JSON: { text: string; number: number };
👨💻About the author
I'm Gergely Szerovay, leading frontend development as a chapter lead. Angular is both my profession and my hobby—I'm constantly absorbing new material about it, from articles and podcasts to conference sessions and beyond.
To share what I discover, I launched the Angular Addict Newsletter, delivering the finest resources I stumble upon every month. Whether Angular is brand new to you or you've been using it for years, there's something valuable waiting inside.
Beyond the newsletter, I curate a publication named Angular Addicts, housing the most insightful and engaging resources I've encountered. Interested in contributing? Just give me a shout.
Want to master Angular alongside me? Sign up right here 🔥
Catch me on Substack, Medium, Dev.to, Twitter, or LinkedIn for more Angular insights!
