'ion-select is not showing selected value with reactive forms

When i use formcontrolname on an ion-select element, the selected value is not shown from the beginning. It only appears when i click the select element, or if I use setTimeout():

html:

<form [formGroup]="formGroup">
            <ion-row>
                <ion-col>
                    <ion-select okText="{{ 'common.ok' | translate }}"
                                cancelText="{{ 'common.close' | translate }}"
                                interface="action-sheet"
                                formControlName="absenceTypeId">
                        <ion-select-option *ngFor="let absenceType of absenceTypes" value="{{absenceType.id}}">{{absenceType.name}}</ion-select-option>
                    </ion-select>
                </ion-col>
            </ion-row>
        </form>

typescript:

@Input() absenceTypeId: string;

formGroup = this.formBuilder.group({
    id: [null],
    absenceTypeId: [null, Validators.required],
    beginDate: [null, Validators.required],
    endDate: [null]
});


ngOnInit() {
            this.formGroup.patchValue({
                absenceTypeId: this.absenceTypeId
            });
}

Is this a bug, or should the formgroup be changed in another event than ngOnInit()?



Solution 1:[1]

In the OnInit you should use the FormBuilder to initialize your form.

this.formGroup = this.formBuilder.group({ absenceTypeId: this. absenceTypeId });

Where the formBuilder it's injected into your constructor. You can keep the current implementation but first you should initialize your form. In your constructor you should have something like previous example:

this.formGroup = this.formBuilder.group({ absenceTypeId: this. absenceTypeId });

And then you can keep your code from OnInit.

EDIT:

You can check this example https://stackblitz.com/edit/ionic-angular-v5-gmwmrw?file=src%2Fapp%2Fapp.component.ts.

As you can see, if you're putting a different value than the rest of the values from ion-select when you patch your form, you'll not have a default value displayed. So, check if your input absenceTypeId it's a value from your absenceTypes. Additional, add a compareWith function because you're iterating over objects.

Solution 2:[2]

So the issue seems to have been, that the patchValue was happening before all the items for the select were loaded. In plain Angular this was never an issue, because the control was updated / rerendered even when the items were loaded after the selected value was defined. For Ionic I was able to solve this by awaiting the items and only after that do the patchValue:

absenceTypes$ = new ReplaySubject<AbsenceTypeModel[]>(1);

ngOnInit() {
    this.absenceTypes$
        .subscribe(() => {
            if (this.absenceTypeId) {
                this.formGroup.patchValue({
                    absenceTypeId: this.absenceTypeId
                });
            }
        });

    this.absenceTypeService.getAll()
        .subscribe(absenceTypes => this.absenceTypes$.next(absenceTypes));
}

I'm not sure if this is the intended behaviour of ionic but it will definitely make development a bit more complicated.

EDIT: Also you should use [value] instead of value for non string bindings. Otherwise it will compare string to non-string which will not be equal:

<ion-select-option [value]="absenceType.id">

Solution 3:[3]

Another approach is to add the [value] property to the <ion-select> element. I'm not sure if it is the intented way, but the Ionic documentation says:

value
Description the value of the select.
Attribute value
Type any
Default undefined

So you can do something like this instead:

app.component.html

<ion-app>
  <ion-header>
    <ion-toolbar>
      <ion-title>Hello</ion-title>
    </ion-toolbar>
  </ion-header>
  <ion-content>
    <app-test personId="1"></app-test>
  </ion-content>
</ion-app>

test.component.html

<form [formGroup]="form">
  <ion-row>
    <ion-col>
      <ion-select
        okText="ok"
        cancelText="cancel"
        interface="action-sheet"
        [compareWith]="compareWithFn"
        formControlName="personId"
        [value]="personId"
      >
        <ion-select-option *ngFor="let person of values" [value]="person.id">{{
          person.name
        }}</ion-select-option>
      </ion-select>
    </ion-col>
  </ion-row>
</form>

Stackblitz example: https://stackblitz.com/edit/ionic-angular-v5-nkg8tu

Solution 4:[4]

ngAfterViewInit(){
            this.formGroup.patchValue({
                absenceTypeId: this.absenceTypeId
            });
}

Solution 5:[5]

In Your Html File

<form [formGroup]="formGroup" (ngSubmit)="onSubmit()">
    <ion-row>
         <ion-col>
             <ion-select okText="{{ 'common.ok' | translate }}" cancelText="{{ 'common.close' | translate }}" interface="action-sheet" formControlName="absenceTypeId">
                 <ion-select-option *ngFor="let absenceType of absenceTypes" value="{{absenceType.id}}">{{absenceType.name}}</ion-select-option>
              </ion-select>
         </ion-col>
    </ion-row>
</form>

In Your Ts File

onSubmit(){

   let absenceTypeId = this.formGroup.value.absenceTypeId;
   console.log(absenceTypeId);

}

Try this code.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3 Mario Werner
Solution 4 Zrelli Majdi
Solution 5 Bhuvanesh Cj