Search code examples
angularsignals

Issue with Change Detection in Angular 17 Signals for List-Type Signal (Migration from Angular 16)


Recently migrated my app to Angular 17 and have encountered some issues with signals. In version 16, I created a list-type signal that worked perfectly, but now, in version 17, there seems to be an issue with change detection. I'm wondering if it could be an error on my part in how I'm using signals. Do you have any advice or suggestions on how to address this issue in the new version?

import { Component, effect, signal } from '@angular/core';

@Component({
  selector: 'app-test-signals',
  standalone: true,
  imports: [],
  template: `
    <div>
        <button (click)="sum()"> add </button>
    </div>
  `,
  styles: ``
})
export class TestSignalsComponent {
  listObjectSignals = signal<{id:number, name:string}[]> ([])
  objectSignal = signal<{id:number, name:string}> ({id:0, name:'0'})
  cnt = 1;
  
  constructor(){
    effect(()=>{
      console.log(`List signal not triggered: ${this.listObjectSignals()}`)
    })
    effect(()=>{
      console.log(`Object signal triggered successfully: ${this.objectSignal()}`)
    })
  }

  sum(){
    let tempList: {id:number, name:string}[] = this.listObjectSignals();
    this.cnt = this.cnt + 1
    tempList.push({id:this.cnt, name:this.cnt.toString()})
    this.objectSignal.set({id:this.cnt, name:this.cnt.toString()})
    this.listObjectSignals.set(tempList)
    console.log(this.listObjectSignals(), tempList)
  }
}

Solution

  • The main breaking change of Angular 17.0.0-next.8 release is default equality check function in signals has been replaced, and now it’s just Object.is()

    In version 16 function would consider any two objects as non-equal so if in signal.set() you use objects, and your signal.update() or computed() return objects — you would always receive a notification, and the UI would be always updated.

    If you want to recive a notification you need to change the reference of the object

    sum(){
     let tempList: {id:number, name:string}[] = this.listObjectSignals();
     this.cnt = this.cnt + 1
     tempList.push({id:this.cnt, name:this.cnt.toString()})
     this.objectSignal.set({id:this.cnt, name:this.cnt.toString()})
     this.listObjectSignals.set(structuredClone(tempList))
     console.log(this.listObjectSignals(), tempList)
    }
    

    For More Information