typescript - angular signal is not recomputing - Stack Overflow

时间: 2025-01-06 admin 业界

I have this code in my constructor

  public constructor() {
    this.dropdown = signal([
      {
        name: 'Normal Text',
        tag: 'p',
        selected: true
      },
      {
        name: 'Heading 1',
        tag: 'h1',
        selected: false,
      },
      {
        name: 'Heading 2',
        tag: 'h2',
        selected: false
      },
      {
        name: 'Heading 3',
        tag: 'h3',
        selected: false
      },
      {
        name: 'Heading 4',
        tag: 'h4',
        selected: false
      },
      {
        name: 'Heading 5',
        tag: 'h5',
        selected: false
      },
      {
        name: 'Heading 6',
        tag: 'h6',
        selected: false
      }
    ]);

    this.selectedTextType = computed(() => this.dropdown().find(x => x.selected) as DropdownOption);
  }

and when an option is clicked from the dropdown this code is executed

  public textTypeChange(option: DropdownOption, e: Event): void {
    e.preventDefault();

    this.selectedTextType().selected = false;
    option.selected = true;
  }

The selectedTextType signal does not get recomputed. The dropdown option does not change in the html. What am I doing wrong?

I have this code in my constructor

  public constructor() {
    this.dropdown = signal([
      {
        name: 'Normal Text',
        tag: 'p',
        selected: true
      },
      {
        name: 'Heading 1',
        tag: 'h1',
        selected: false,
      },
      {
        name: 'Heading 2',
        tag: 'h2',
        selected: false
      },
      {
        name: 'Heading 3',
        tag: 'h3',
        selected: false
      },
      {
        name: 'Heading 4',
        tag: 'h4',
        selected: false
      },
      {
        name: 'Heading 5',
        tag: 'h5',
        selected: false
      },
      {
        name: 'Heading 6',
        tag: 'h6',
        selected: false
      }
    ]);

    this.selectedTextType = computed(() => this.dropdown().find(x => x.selected) as DropdownOption);
  }

and when an option is clicked from the dropdown this code is executed

  public textTypeChange(option: DropdownOption, e: Event): void {
    e.preventDefault();

    this.selectedTextType().selected = false;
    option.selected = true;
  }

The selectedTextType signal does not get recomputed. The dropdown option does not change in the html. What am I doing wrong?

Share Improve this question edited yesterday JSON Derulo 17.1k10 gold badges56 silver badges73 bronze badges asked yesterday adamadam 4651 gold badge7 silver badges15 bronze badges 2
  • 1 This is object oriented development. You do not change the value of the signal, that s why computed is not triggered. Instead of manipulating properties of parts of your signals value, you should use the update method of the signal. – Thomas Renger Commented yesterday
  • The dirty way would be this.dropdown.set(...this.dropdown()). But without konwing the details i would recommend the version in my first comment. – Thomas Renger Commented yesterday
Add a comment  | 

1 Answer 1

Reset to default 1

By default, Angular signals use referential equality to track whether the value has changed. If you are storing objects or arrays in your Signal, this means that the value is treated as unchanged as long as the reference is the same. If you are updating an object inside your array, the reference remains unchanged. Angular thinks that the value is not changed and thus the template doesn't update, which is by design.

To fix it, I strongly recommend to separate the data from the selection state. For example, you can simply store the tag of the selected value in a separate signal:

this.dropdown = signal([
  {
    name: "Normal Text",
    tag: "p",
  },
  {
    name: "Heading 1",
    tag: "h1",
  },
  // ...
]);
this.selectedTextType = signal('p');

Another approach would be to install @angular/cdk in your project and use their SelectionModel. The code should look somewhat like the following:

this.dropdown = signal([
  {
    name: "Normal Text",
    tag: "p",
  },
  {
    name: "Heading 1",
    tag: "h1",
  },
  // ...
]);
this.selectionModel = new SelectionModel(false, ['p']);