Search code examples
angularangular-changedetection

select component dont render values untlil second interaction


The select is not rendering data until the second interaction with him.

<div class="align-items-center">
          <label class="form-label">Select Value</label>
          <div class="input-group">
            <select formControlName="idUsuario" class="form-select" aria-label="Select Value">
              <option value="">-- Value--</option>
              @for (value of valueList; track usuario.id) {
                <option [value]="value .id">{{ value .option}} {{ value .option2}}</option>
              }
            </select>
          </div>

        </div>

The thing is that when the page is loaded the select don't have any value. I click it first time and still don't have values. When i move the focus to other part of the page and then click the select for second time, it has the values. What could it be?

Here is the relevant code in the component.

  public valueList: Value[] = [];

  ngOnInit(): void {
    this.valueServie.ggetValues().subscribe((values) => {
      this.valueList = values;
    });
  }


Solution

  • When using the change detection strategy OnPush, your component's DOM will only be updated when one of the following applies:

    • An input changes
    • An event listener runs
    • The component was marked for check via ChangeDetectorRef

    In your case, none of these apply, that's why the DOM is not updated right away. When interacting with the elements in your component, event listeners run, which trigger a DOM update. There are multiple ways to fix it:

    1. Inject ChangeDetectorRef and call markForCheck() after the data was set.

      constructor(private readonly cdr: ChangeDetectorRef) {}
      
      ngOnInit(): void {
        this.valueServie.ggetValues().subscribe((values) => {
          this.valueList = values;
          this.cdr.markForCheck();
        });
      }
      
    2. Use the async pipe which marks the component for check under the hood once the Observable pushes new values:

      public valueList$: Observable<Value[]>;
      
      constructor() {
        this.valueList$ = this.valueServie.ggetValues();
      }
      
      <select>
        @for (value of valueList$ | async; track usuario.id) {
          <option [value]="value.id">
            {{ value.option}} {{value.option2}}
          </option>
        }
      </select>
      
    3. Use Default change detection strategy. Note that this will highly increase the number of DOM updates and may lead to degraded performance.