Search code examples
angularselectdata-bindingvmware-clarity

Angular 5, Clarity Design - Select options "disappear" when pushing new item to options array


I'm working on a project with Angular 5 using Clarity Design as a CSS framework.

On a select I'm binding its options dynamically and a weird thing happens when binding a new item.

Basically the new item gets added to the DB, and inside the subscription callback is where the binding gets done. When that happens the select looks like no option has been selected, the binding is being made correctly but that visual aspect is what I'm stuck with now.

Here's how the select is like on the markup:

<div class="row">
    <div class="col-xs-12">
      <div
        class="select"
        [class.disabled]="contextResources.length < 1">
        <select
          [disabled]="contextResources.length < 1"
          (change)="emitSelectedResource(optionSelected)"
          [(ngModel)]="optionSelected">
          <option *ngIf="contextResources.length < 1">Agrega un {{ context.name | lowercase}} nuevo</option>
          <option
            *ngFor="let contextResource of contextResources"
            [ngValue]="contextResource">
          {{ contextResource.name }}</option>
        </select>
      </div>
    </div>
  </div>

And the component method:

addResource(): void {
    this.isLoading = true;
    this.resourceAdded = false;
    this.resourceError = false;

    let parentId = this.previousResource ? this.previousResource.id : null;

    this.resourceServices[this.currentStep].create({'newResource': this.newResource}, parentId)
      .finally(() => this.isLoading = false)
      .subscribe(
        response => {
          this.contextResources.push(response.json().newResource as ContextResource);

          if(this.contextResources.length == 1)
            this.emitSelectedResource(this.contextResources[0]);

          this.newResource = '';
          this.resourceAdded = true;
          this.emptyResources.emit(false);
        },
        (error: AppError) => {
          if(error instanceof BadRequestError)
            return this.resourceError = true;

          throw error; 
        }
      );
  }

This is how the select looks like with some options, all normal:

enter image description here

Now after adding a new item:

enter image description here

And if we take a look inside the list:

enter image description here

Is the a way to prevent this behavior?


Solution

  • Yes, the contextResources are brand new objects every time, and select checks the selected one based on reference equality by default.

    The answer is to use the [compareWith] input, as explained here: https://angular.io/api/forms/SelectControlValueAccessor#caveat-option-selection

    <select [compareWith]="compareFn"  [(ngModel)]="selectedCountries">
        <option *ngFor="let country of countries" [ngValue]="country">
            {{country.name}}
        </option>
    </select>
    
    compareFn(c1: Country, c2: Country): boolean {
        return c1 && c2 ? c1.id === c2.id : c1 === c2;
    }