2
votes

recently I'm trying to create a custom dropdown component using value accessor but I can't set a value to this dropdown.

This part of the dropdown component

    <mat-form-field>
      <mat-label>Favorite food</mat-label>
      <mat-select>
        <mat-option *ngFor="let food of foods" [value]="food.value">
          {{food.viewValue}}
        </mat-option>
      </mat-select>
    </mat-form-field>

ts part


    @Input() foods:any
    get value(): any {
        return this.innerValue;
      }

      // set accessor including call the onchange callback
      set value(v: any) {
        console.log(v, "v");
        if (v !== this.innerValue) {
          this.innerValue = v;
          this.onChangeCallback(v);
        }
      }

App Component HTML:

    <app-input-dropdown [foods]="foods" formControlName="dropdown">
          </app-input-dropdown>

ts :

     this.foods = [
          { value: "steak-0", viewValue: "Steak" },
          { value: "pizza-1", viewValue: "Pizza" },
          { value: "tacos-2", viewValue: "Tacos" }
        ];

     ngOnInit() {
    this.myForm = this.fb.group({
    dropdown:[null]
    })
    const valueToSet = { value: "steak-0", viewValue: "Steak" }
        this.myForm.dropdown.patchValue({
    dropdown:valueToSet
    }) ;
      }

So this is not working, I don't know why.

1

1 Answers

0
votes

I can think of few changes/additions to your code. First, you should change the way of how you set value to the form:

const valueToSet = { value: "steak-0", viewValue: "Steak" }
        this.myForm.dropdown.patchValue({
    dropdown:valueToSet
    }) ;

Executing this will produce an error because there should be no 'dropdown' keyword inside this.myForm.dropdown.patchValue. It should be - this.myForm.patchValue. But even after correcting this, the right way to set a value is:

this.myForm.controls["dropdown"].setValue("pizza-1");

or when using patchValue:

this.myForm.patchValue({dropdown: 'pizza-1'});

that is, to change the value of the select list programmatically, you should base on the id and not on the object itself.

Next is to make sure that you have writeValue() method in your custom control:

 writeValue(val: string): void {
    this.selectedOption = val;
  }

Then finally, bind the selectedOption (or innerValue in your case) to the mat-select tag:

  <mat-select placeholder="Favorite food" [(ngModel)]="selectedOption">
    <mat-option *ngFor="let food of foods" [value]="food.value">
      {{food.viewValue}}
    </mat-option>
  </mat-select>

No need to change anything in your app component html. It will still be using Reactive forms:

<app-input-dropdown [foods]="foods" formControlName="dropdown">
          </app-input-dropdown>

I can provide a sample in Stackblitz if you will still encounter a problem. Just let me know.