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: clearScreen option support
  • Angular CLI: define option 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);
  }
}
Enter fullscreen mode Exit fullscreen mode

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('');
}
Enter fullscreen mode Exit fullscreen mode

📌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}

Enter fullscreen mode Exit fullscreen mode

📌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,
            // ...            
Enter fullscreen mode Exit fullscreen mode

📌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 };
Enter fullscreen mode Exit fullscreen mode

👨‍💻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!