Search code examples
angulartypescriptsignals

Angular Behavior: Property Type Changes from SignalFunction to Boolean


I’m encountering an interesting behavior in my Angular application, and I’m hoping someone can shed some light on it. Here’s the scenario: I have a working code snippet where I define a property called isSelection like this:

public isSelection = model.required<boolean>();
public getTypeof(value: any): string {
    return typeof value;
}

The isSelection property is initialized with a model.required signal

<input type="checkbox" [(ngModel)]="isSelection" />
<p>{{ getTypeof(isSelection) }}</p>

I bind the isSelection property to a checkbox input using [(ngModel)]

The paragraph displays the type of isSelection. In this case the isSelection is displayed as a function when i press the checkbox the type of the isSelection never changed.

However, when I change the property declaration to:

public isSelection = input.required<boolean>();

The behavior changes, and the type of isSelection seems to switch from a function to a boolean.

NB I'm using the last version of angular 17.3.1.


Solution

  • input.required<boolean>() -> meant for one way binding, but you are using for two way binding, so the function gets replaced by a value that is boolean

    To remedy this, we can use one way binding in HTML

    <input type="checkbox" [ngModel]="isSelection2" />
    

    When I use the code provided as a child and provide inputs from the parent, angular just throws an error when I try to use [(ngModel)]="isSelection2"

    model.required<boolean>() -> meant for two way binding, so your example works perfectly, since it does exactly what it's supposed to do!

    See the below code/Stackblitz:

    child

    import { Component, input, model } from '@angular/core';
    import { FormsModule } from '@angular/forms';
    
    @Component({
      selector: 'app-child',
      standalone: true,
      imports: [FormsModule],
      template: `
        <input type="checkbox" [(ngModel)]="isSelection" />
        <pre>{{ isSelection() }}</pre>
        <p>{{ getTypeof(isSelection) }}</p>
        <hr />
        <input type="checkbox" [ngModel]="isSelection2" />
        <pre>{{ isSelection2() }}</pre>
        <p>{{ getTypeof(isSelection2) }}</p>
      `,
    })
    export class ChildComponent {
      public isSelection = model.required<boolean>();
      public isSelection2 = input.required<boolean>();
    
      public getTypeof(value: any): string {
        return typeof value;
      }
    }
    

    main.ts

    import { Component } from '@angular/core';
    import { bootstrapApplication } from '@angular/platform-browser';
    import 'zone.js';
    import { ChildComponent } from './app/child/child.component';
    
    @Component({
      selector: 'app-root',
      standalone: true,
      imports: [ChildComponent],
      template: `<app-child [isSelection2]="true" [isSelection]="false"/>`,
    })
    export class App {}
    
    bootstrapApplication(App);
    

    Working Stackblitz