Search code examples
angularsignalsangular2-changedetectionangular-signalsngonchanges

Can I still use ngChanges with signals?


With the traditional @Input() decorator, one can use the ngChanges to do something if the value change. In this case, I'm using a component in the ng-bootstrap modal. The modal contains a form. When the item to edit is selected, it's passed to the component that's in the modal.

@Input() inputVar!: SomeType;

ngOnChanges(changes: SimpleChanges): void {
  const chInputVar = changes['inputVar'];

  //Do something when the value changes...
}

Now, I'd like to use the signal input:

inputVar = input<SomeType>();
//How to act when the value changes?

Solution

  • ngOnChanges fires for all input changes. Instead go for effect which fires only when the signals inside the effect.

    @Component({
      selector: 'app-child',
      standalone: true,
      template: ``,
    })
    export class Child {
      inputVar = input<number>();
    
      constructor() {
        effect(() => {
          const value = this.inputVar();
          console.log('signal change', value);
          // Perform the signal change logic here!
        });
      }
    }
    

    Stackblitz Demo


    If you must use ngOnChanges then below is a working example for the same.

    import { Component, effect, input, SimpleChanges } from '@angular/core';
    import { bootstrapApplication } from '@angular/platform-browser';
    import 'zone.js';
    
    @Component({
      selector: 'app-child',
      standalone: true,
      template: ``,
    })
    export class Child {
      inputVar = input<number>();
    
      constructor() {}
    
      ngOnChanges(changes: SimpleChanges) {
        console.log(changes);
        const inputVar = changes?.['inputVar'];
        if (inputVar?.currentValue !== inputVar?.previousValue) {
          console.log('change', inputVar);
        }
      }
    }
    
    @Component({
      selector: 'app-root',
      standalone: true,
      imports: [Child],
      template: `
        <app-child [inputVar]="value" />
      `,
    })
    export class App {
      value = 0;
    
      ngOnInit() {
        setInterval(() => {
          this.value = Math.random();
        }, 1000);
      }
    }
    
    bootstrapApplication(App);
    

    Stackblitz Demo