adam
adam

Reputation: 483

angular signal is not recomputing

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?

Upvotes: 0

Views: 89

Answers (1)

JSON Derulo
JSON Derulo

Reputation: 17758

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']);

Upvotes: 1

Related Questions