[Ionic 5] [Angular] ion-select does not show the selected values

Ionic:

Ionic CLI : 6.0.0
Ionic Framework : @ionic/angular 5.0.0
@angular-devkit/build-angular : 0.801.3
@angular-devkit/schematics : 8.1.3
@angular/cli : 8.1.3
@ionic/angular-toolkit : 2.1.2

You can see on the gif below, when I try to edit the modal, nothing is selected on “Coletor”. However, after clicking on “Coletor” the “Linha 02” is already selected. It works properly with the field “Permissão” though:

screencast-localhost_8100-2020.04.05-15_43_19

config-add-user.page.html

<ion-header>

  <ion-toolbar color="primary">
    <ion-buttons slot="start">
      <ion-button (click)="closeModal()">
        <ion-icon name="arrow-back-outline"></ion-icon>
      </ion-button>
    </ion-buttons>
    <ion-title><b>Usuário</b></ion-title>
  </ion-toolbar>

</ion-header>

<ion-content class="ion-padding">

  <form [formGroup]="addUserGroup">
    <ion-list inset ion-no-border>

      <ion-item>
        <ion-label color="medium">Usuário:</ion-label>
        <ion-input class="ion-text-end" type="text" name="username" formControlName="username" value="{{ username }}"></ion-input>
      </ion-item>

      <br>

      <ion-item>
        <ion-label color="medium">Password:</ion-label>
        <ion-input class="ion-text-end" type="password" name="password" formControlName="password" value="{{ password }}"></ion-input>
      </ion-item>

      <br>

      <ion-item>
        <ion-label color="medium">Name:</ion-label>
        <ion-input class="ion-text-end" type="text" name="name" formControlName="name" value="{{ name }}"></ion-input>
      </ion-item>

      <br>

      <ion-item>
        <ion-label color="medium">Permissão:</ion-label>
        <ion-select class="ion-text-end" name="permission" formControlName="permission" value="{{ selectPermission }}">
            <ion-select-option value="1">Usuário</ion-select-option>
            <ion-select-option value="2">Administrador</ion-select-option>
        </ion-select>
        <ion-icon name="chevron-forward-outline" color="primary" item-end></ion-icon>
      </ion-item>

      <br>

      <ion-item>
        <ion-label color="medium">Coletor:</ion-label>
        <ion-select class="ion-text-end" multiple="true" name="idLine" formControlName="idLine" [(value)]="selectLine">
          <ion-select-option *ngFor="let item of linesList" value="{{item.id}}">
            {{ item.name }}
          </ion-select-option>
        </ion-select>
        <ion-icon name="chevron-forward-outline" color="primary" item-end></ion-icon>
      </ion-item>

      <br>
    
    </ion-list>

    <ion-row>
      <ion-col>
        <ion-button [disabled]="addUserGroup.invalid" (click)="postUser()" type="submit" expand="block" size="large">SALVAR</ion-button>
      </ion-col>
    </ion-row>

  </form>

</ion-content>

config-add-user.page.ts

/*****************************************************/
/******************* GENERAL IMPORTS *****************/
/*****************************************************/

import { UserService } from '../api/user.service';
import { Component, Input } from '@angular/core';
import { Validators, FormBuilder, FormControl } from '@angular/forms';
import { AlertController, ModalController } from '@ionic/angular';

@Component({
  selector: 'app-config-add-user',
  templateUrl: './config-add-user.page.html',
  styleUrls: ['./config-add-user.page.scss'],
})

export class ConfigAddUserPage {

  /* DATA VARIABLES */
  addUserGroup: any = {};
  linesList: Array<any>;

  /* EDIT VARIABLES */
  selectLine: Array<any>;
  selectPermission: string;

  // Data passed in by componentProps
  @Input() modal: any;
  @Input() edit: string;
  @Input() token: string;
  @Input() id: string;
  @Input() idLine: Array<any>;
  @Input() clientId: string;
  @Input() username: string;
  @Input() password: string;
  @Input() name: string;
  @Input() permission: string;

  constructor(
    public service : UserService,
    public alertCtrl: AlertController,
    public formBuilder : FormBuilder,
    public modalController: ModalController,
  ) {

      /*****************************************************/
      /****************** FORM VALIDATOR *******************/
      /*****************************************************/

      this.addUserGroup = this.formBuilder.group({
        token: new FormControl('xxxx', Validators.required),
        dns: new FormControl(this.service.getDns(), Validators.required),
        user: new FormControl(this.service.getUser(), Validators.required),
        pass: new FormControl(this.service.getPass(), Validators.required),
        clientId: new FormControl(this.service.getClientId(), Validators.required),
        id: new FormControl(this.id),
        username: new FormControl('', Validators.required),
        password: new FormControl('', Validators.required),
        name: new FormControl('', Validators.required),
        permission: new FormControl('', Validators.required),
        idLine: new FormControl('', Validators.required)
      });
  }

  /*****************************************************/
  /****************** ONINIT FUNCTIONS *****************/
  /*****************************************************/

  ionViewDidEnter() {
    this.getProductionLines();
    this.selectPermission = this.permission;
    this.selectLine = this.idLine;

    // Removing Ion Select Icons
    const ionChange = document.querySelectorAll('ion-select');
    ionChange.forEach((sel) => {
      sel.shadowRoot.querySelectorAll('.select-icon-inner')
        .forEach((elem) => {
          elem.setAttribute('style', 'display: none;');
        });
    });
  }

  /*****************************************************/
  /****************** POST OR UPDATE *******************/
  /*****************************************************/

  postUser() {
    if (this.edit == "true") {
      this.service.updateDataUser(this.addUserGroup.value)
        .subscribe(
          data=>{
            console.log(data.message);
        }, 
          err=>console.log(err)
      );
      this.modal.dismiss();
    } 
    if (this.edit == "false") {
      this.service.postDataUser(this.addUserGroup.value)
        .subscribe(
          data=>{
            if (data.search === false) {
              console.log(data.message);
              this.modal.dismiss();
            } else {
              console.log(data.message);
              this.postAlert();
            }
        },
          err=>console.log(err)
      );
      this.modal.dismiss();
    }
  }

  async postAlert() {
    const alert = await this.alertCtrl.create({
      header: 'Usuário Repetido',
      message: 'Tente outro username...',
      buttons: ['OK']
    });
    await alert.present();
  }

  /******************************************************/
  /******************* API - GET DATA *******************/
  /******************************************************/

  getProductionLines() {
    this.service.getDataProductionLines().subscribe(
      data => this.linesList = data,
      err => console.log(err)
    );
  }

  /******************************************************/
  /******************** CLOSE MODAL ********************/
  /******************************************************/

  closeModal() {
    this.modal.dismiss({
      'edit' : "",
      'token' : "", 
      'id' : "", 
      'idLine' : "", 
      'clientId' : "", 
      'username' : "", 
      'password' : "",
      'name' : "" ,
      'permission' : "",
    });
  }

}

Any ideas? :slight_smile:

2 Likes

My idea is “don’t mix binding methods for form controls”. If you have formControlName, get rid of all value bindings. Having more than one source of truth for the form control causes infighting.

Hello, @rapropos! Thank you!

I tried to this approach and unfortunately it did not work. Actually it stopped showing the values after clicking as well.

      <ion-item>
        <ion-label color="medium">Coletor:</ion-label>
        <ion-select class="ion-text-end" multiple="true" name="idLine" formControlName="idLine">
          <ion-select-option *ngFor="let item of linesList" value="{{item.id}}">
            {{ item.name }}
          </ion-select-option>
        </ion-select>
        <ion-icon name="chevron-forward-outline" color="primary" item-end></ion-icon>
      </ion-item>

I think the problem is somehow related to the <*ngFor=“let item of linesList”>, because “selectLine” is returning the right values.

Any other ideas? I have been trying different possibiliteis for days already, hehe!

Thank you!

1 Like

See if the following code behaves as you are hoping. If it doesn’t, then there’s something different between our environments or I’m misunderstanding your problem. If it does, then see if you can isolate what’s different between it and your problematic code.

interface Fruit {
  id: string;
  name: string;
}

export class HomePage {
  idLine = new FormControl();
  linesList: Fruit[] = [];

  constructor(private loaders: LoadingController) {
  }

  simulateBackend(): Observable<Fruit[]> {
    return timer(3000).pipe(map(() => [
      {id: "a", name: "apple"},
      {id: "b", name: "banana"},
      {id: "c", name: "cherry"},
    ]));
  }

  fill(): void {
    this.loaders.create().then(loader => loader.present());
    this.simulateBackend().pipe(
      finalize(() => this.loaders.dismiss()))
      .subscribe(fruits => this.linesList = fruits);
  }
}
<ion-content>
  <ion-item>
    <ion-label color="medium">Coletor:</ion-label>
    <ion-select class="ion-text-end" multiple="true" name="idLine" [formControl]="idLine">
      <ion-select-option *ngFor="let item of linesList" value="{{item.id}}">
        {{ item.name }}
      </ion-select-option>
    </ion-select>
    <ion-icon name="chevron-forward-outline" color="primary" item-end></ion-icon>
  </ion-item>
  <ion-item button (click)="fill()"><ion-label>fill</ion-label></ion-item>
</ion-content>

Thank you! I am trying to isolate what’s different!

In the meanwhile I made a test with the following code (getting rid of the ngFor) and it worked properly:

      <ion-item>
        <ion-label color="medium">Coletor:</ion-label>
        <ion-select class="ion-text-end" multiple="true" name="idLine" formControlName="idLine" [value]="selectLine">
          <ion-select-option value="1">Teste 1</ion-select-option>
          <ion-select-option value="2">Teste 2</ion-select-option>
          <ion-select-option value="3">Teste 3</ion-select-option>
        </ion-select>
        <ion-icon name="chevron-forward-outline" color="primary" item-end></ion-icon>
      </ion-item>

In order to clarify my issue I made the gif below You can see that after clicking on “Coletor” the “Linha 02” is already selected although it wasn’t being showed before. It works properly with the field “Permissão” though.

screencast-localhost_8100-2020.04.05-15_43_19

Anyway, I will use what you shared to keep investigating. If you have any ideas I do appreciate! :slight_smile:

Thank you!

2 Likes

Hello, @rapropos! Thank you again for your support. Unfortunately I didn’t manage to find the solution yet :frowning:

Hello, @All!

Does anyone have an idea about how to fix it? I have searched everywhere, hehe! :pray:

I have added more information on the first topic.

Thank you! :slight_smile:

hello i faced the same problem , i used a SetTimout function to resolve it

 setTimeout(() => {
        this.form.setValue({
          profile_name: this.profile_name,
          profile_surname: this.profile_surname,
          branch_id: ["" + this.branch_id + ""]
        });

      }, 500);
3 Likes

Still doesn’t work for me. Any other ideas? When data comes from the server the formcontrol value does not show correctly until you click on the select control as the original issue described.

ran through similar issue as you all.
I noticed that the value get display after clicking on the ion-select, even if you don’t select a new value.

So I tried to dirty the form but still no success.

My solution was to use [ selectedText]="form.get('wantedField').value"

7 Likes

Hello kaiovm,
I hope you already fixed your issue :slight_smile:
if not or for any other guy, I also ran into this “error” and in my special case, there was a “race condition” between loading server data in ngOnInit and setting the ion-select [(ngModel)] in ionViewDidEnter.
I had to ensure, that server data is loaded before ngModel is set.

While it may not be the correct way to do it, I use ngIf on the ion-select element to check if the array is null. I was experiencing the same issue as you.

Why not just initialize the backing array to []? I highly recommend adding the following to the compilerOptions stanza of tsconfig.json:

"noImplicitAny": true,
"strictNullChecks": true,
"strictPropertyInitialization": true

This way, the build process yells at me when I do this:

blowUpTheTemplate: Thingy[];

…reminding me to:

noLongerExplosive: Thingy[] = [];
2 Likes

hi, i have the same problem,
I do an setTimeout before pass the data to formControl like this:

setTimeout(() => {
        this.createAgentForm.patchValue(tempAgent);
}, 900);

I hope it is useful to you.

1 Like

Dear future reader of this thread,

Please ignore all the people suggesting setTimeout with magic numbers. At best, it’s inefficient. At worst, it’ll work sometimes in some environments, and you won’t ever know when you’ve set it correctly.

Upthread, I posted code that anybody should be able to toss into their project, that needs no setTimeout in the client. The magic number of 3000 is arbitrary, intended to simulate the delay of a network. It is not integral to the solution. You can change it at will, or even eliminate it. If you are having problems, see if that code behaves differently from what you have, and search for differences.

4 Likes

Thank you! This worked for me.

1 Like

My issue was simply that I needed to load the selection items before I initialized the form otherwise it had the value but was not displaying it as selected until it was clicked on.

I am pretty sure my code was working in Ionic 4 but didn’t in Ionic 5 and so I had to wait to initialize and display the form until I had all my selection item data.

If you’d like to post code, I’ll look at it, but I’d be very surprised if Angular change detection didn’t properly handle this situation, which I would expect to be very common.

Great thanks. Hopefully I haven’t missed anything below.
I didn’t need to subscribe as I just needed to get it once.

Code before (problem as above:

 <form [formGroup]="memberForm" (ngSubmit)="onUpdatePersonal()" #formCtrl="ngForm">
//...
 <ion-select placeholder="Age Group" formControlName="ageGroupId">
              <ion-select-option *ngFor="let ageGroup of ageGroups" [value]="ageGroup.id" selected="false">{{ageGroup.name}}</ion-select-option>
            </ion-select>
//...
</form>


async ngOnInit() {
  this.initForm();
  this.settingsService.getAgeGroups().subscribe(ageGroups => {
          this.ageGroups = ageGroups;
        });
}

initForm() {
  this.memberForm = this.fb.group({
  // ... Other controls here
      ageGroupId: new FormControl(
        this.member.ageGroupId ? this.member.ageGroupId : '',
        Validators.compose([Validators.required])),
    });
}

Code after: now working (not ideal) - Yes I will use an await when I refactor ;):

ngOnInit() {
  this.settingsService.getAgeGroups().then(ageGroups => {

          this.ageGroups = ageGroups;

          if (this.member.membershipPackage.code === 'adult') {

            // Remove 17 and under from Adult

            this.ageGroups.splice(this.ageGroups.findIndex(x => x.id === '1'), 1);

          }

          this.initForm();

        });
}
// Added ngIf so the form was ready
<ion-grid *ngIf="member && memberForm" padding class="account-text">

    <form [formGroup]="memberForm" (ngSubmit)="onUpdatePersonal()" #formCtrl="ngForm">
      // other code the same as above
  </form>
</ion-grid>

I would have thought that loading the selection data after should work fine. As I said I think this works in Ionic 4 as this code is from an Ionic 4 app.