Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bug: ion-select doesn't recognize delayed or updated select options #19324

Closed
keo9ren opened this issue Sep 11, 2019 · 37 comments · Fixed by #26667
Closed

bug: ion-select doesn't recognize delayed or updated select options #19324

keo9ren opened this issue Sep 11, 2019 · 37 comments · Fixed by #26667
Labels
package: core @ionic/core package type: bug a confirmed bug report

Comments

@keo9ren
Copy link

keo9ren commented Sep 11, 2019

Bug Report

Ionic version:
[x] 4.9.0

Current behavior:
If ion-select-options are passed with a delay, the selected property is not respected.
This happens e.g. if you get the options via http request.

Expected behavior:
The selected binding should work, with or without, the delay.

Steps to reproduce:
See stackblitz.

Related code:
https://stackblitz.com/edit/ionic-v4-angular-tabs-ecdgvr?file=src%2Fapp%2Fhome%2Fhome.page.ts

@ionitron-bot ionitron-bot bot added the triage label Sep 11, 2019
@liamdebeasi
Copy link
Contributor

Thanks for the issue. Please try this again using compareWith https://ionicframework.com/docs/api/select#object-value-references.

@liamdebeasi liamdebeasi added the needs: reply the issue needs a response from the user label Sep 11, 2019
@ionitron-bot ionitron-bot bot removed the triage label Sep 11, 2019
@keo9ren
Copy link
Author

keo9ren commented Sep 12, 2019

Thanks for the tip. I modified the stackblitz. Using compareWith the value is selected, but the ion-select is not rendered correctly. That means the selected value isn't shown, till the select is clicked.

https://stackblitz.com/edit/ionic-v4-angular-tabs-ecdgvr?file=src%2Fapp%2Fhome%2Fhome.page.ts

@ionitron-bot ionitron-bot bot added triage and removed needs: reply the issue needs a response from the user labels Sep 12, 2019
@iamkinetic
Copy link

I'm having the exact same problem with Ionic 4.9.1. 4.7.x and 4.8.x didn't had that issue.

@jorisbertomeu
Copy link

Exact same issue too with Ionic 4.10.0-dev.201909261535.8ca97ce

@keo9ren
Copy link
Author

keo9ren commented Oct 8, 2019

On 4.10.1 the issue is still there. Issue #19436 sounds similar.

@brankoenero
Copy link

I think I found the problem.

@liamdebeasi, why the functionality of the file select.tsx in the line 135 to 157 was removed? It was remove from 4.8.1 to 4.9.1.
48a2763#diff-e954ed7de7ce8c631442335752663627

It seems like it was the responsible of detect changes inside the DOM of select component.

Maybe I can't see by which other functionality it was replaced, please, can you help us?

I hope I provided you enough information.

@Listen('ionSelectOptionDidLoad')
@Listen('ionSelectOptionDidUnload')
async selectOptionChanged() {
  await this.loadOptions();

  if (this.didInit) {
    this.updateOptions();
    this.updateOverlayOptions();
    this.emitStyle();

    /**
     * In the event that options
     * are not loaded at component load
     * this ensures that any value that is
     * set is properly rendered once
     * options have been loaded
     */
    if (this.value !== undefined) {
      this.el.forceUpdate();
    }

  }
}

@keo9ren
Copy link
Author

keo9ren commented Nov 7, 2019

Issue #19828 looks similar.

Any updates from Ionic-Team on this matter?

@liamdebeasi liamdebeasi added package: core @ionic/core package type: bug a confirmed bug report labels Nov 7, 2019
@ionitron-bot ionitron-bot bot removed the triage label Nov 7, 2019
@liamdebeasi
Copy link
Contributor

Thanks for the follow up. I've marked this as a bug. The select component was updated to fix a few other bugs, but it looks like this functionality broke in the process. I will look into an appropriate fix for this and will post an update here when I have more to share. Thanks!

@rathodkaran07
Copy link

@liamdebeasi Any update on this matter?

@jhpetersen
Copy link

Hi there,
i too ran into this issue and was struggeling for two days.
Sadly the made fix mentioned in similar issue #17225 is not solving the problem for Ionic 4 (@ionic/core 4.11.5)!
The issue is easy to reproduce with the following steps:

  1. start a new ionic app with blank starter template

  2. add ReactiveFormsModule to imports within home.module.ts

  3. put the following html inside ion-content within home.page.html:

    <form [formGroup]="formData">
     <ion-item>
       <ion-label>
         Test Text
       </ion-label>
       <ion-input placeholder="type something" formControlName="selectedItem"></ion-input>
     </ion-item>
    
     <ion-item>
       <ion-label>
         Test Select
       </ion-label>
       <ion-select placeholder="select one" formControlName="selectedItem">
         <ion-select-option *ngFor="let item of Items" [value]="item.value">{{ item.text }}</ion-select-option>
       </ion-select>
     </ion-item>
    </form>
    
  4. replace the content of home.page.ts with:

    import { Component, OnInit } from '@angular/core';
    import { FormBuilder, FormGroup } from '@angular/forms';
    
    @Component({
      selector: 'app-home',
      templateUrl: 'home.page.html',
      styleUrls: ['home.page.scss'],
    })
    export class HomePage implements OnInit {
    
      public formData: FormGroup;
    
      public Items: Array<{
        value: string,
        text: string
      }> = [];
    
      constructor(formBuilder: FormBuilder) {
        this.formData = formBuilder.group({
          selectedItem: ''
        });
      }
    
      private async demonstrateAsyncLoadingIssue() {
        this.Items = await new Promise<any>((resolve) => {
          // commect out setTimeout and resolve directly
          // to proof select ui gets updated correctly without delay
          setTimeout(() => {
            resolve([
              {
                value: 'sucks',
                text: 'Damnit, this sucks :('
              },
              {
                value: 'works',
                text: 'Aww yeah, it works :)'
              }
            ]);
          }, 500);
        });
        this.formData.patchValue({ selectedItem: 'sucks' });
      }
    
      ngOnInit() {
        this.demonstrateAsyncLoadingIssue();
     }
    
    }
    
  5. run ionic serve and observe that text in ion-input gets updated after 500ms but the selected item is not. If you click on the select control, you see that the value was set correctly and is displayed/selected. (i didn't include it in this example, but you can subscribe to formControl valueChanges Observable and see that the value is set correctly; also ionChange event on ion-select fires too, but UI does not update)

  6. you can proof that UI is updating correctly without delayed Promise by commenting setTimeout and directly resolve the data.

As for now the only working solutions to this are

  • adding *ngIf="Items && Items.length" to ion-select. In this case evaluating ngIf seems to bypass the problem by re-rendering the control.
  • adding a constructor parameter to ChangedetectorRef and doing a manual detectChanges() after patching formgroup, but this feels kind of fishy 😕

Very annoying, hopefully we see a fix soon...

@fschlaef
Copy link

We are having the same problem with 4.11.6, demo here : https://angular-ionic-4-efei58.stackblitz.io/home

Second menu value is bound correctly, and if you open the menu the value is highlighted correctly, but it doesn't display.

@iamkinetic
Copy link

iamkinetic commented Mar 6, 2020

Bug still exist with 5.0.x. I can confirm that adding *ngIf="items && items.length" to ion-select does bypass the problem.

@mburger81
Copy link
Contributor

This was a problem also on older versions as well, which was fixed previously and now it is back from a while.

We use ngx-formly and have created a merge request to workaround this problem

ngx-formly/ngx-formly#2202

@tatmartins
Copy link

tatmartins commented Apr 29, 2020

Bug still exist with 5.0.x. I can confirm that adding *ngIf="items && items.length" to ion-select does bypass the problem.

How do you deal with this issue if you're using an observable to populate the select options? I had to resort to angular-material's mat-select to get this working.

@mburger81
Copy link
Contributor

Something like that

<ng-container *ngIf="to.options | async; let options">
      <ion-select>
        <ion-select-option *ngFor="let option of options" [value]="option.value">
            {{ option.label }}
        </ion-select-option>
      </ion-select>
    </ng-container>

@nickwinger
Copy link

nickwinger commented May 5, 2020

a major bug still open after half a year. actually to fix it i think it would take just a few minutes. i think this is urgent, because forms just doesn't work. most of the time values are loaded async and boom it doesn't work.
however, this workaround works:

<ion-select *ngIf="(projects$ | async)?.length > 0" interface="popover"
                    [placeholder]="'Choose Project' | translate"
                    formControlName="projectId">
            <ion-select-option *ngFor="let project of projects$ | async"
                               [value]="project.id">{{project.name || project.id}}</ion-select-option>
        </ion-select>

@liamdebeasi
Copy link
Contributor

@nickwinger If you have an idea for how to resolve the issue, would you be interested in making a PR for it? I'd be happy to review it.

@nickwinger
Copy link

@liamdebeasi Well i'm not into the internals of Ionic and how you do the angular wrapping since you moved to stencil, but i know that you implement the interface ControlValueAccessor in angular to be able to support formControlName. Then there are many options how to recognize if the children ion-select-options change. Vanilla DOM would be to make a MutationObserver.
So every time new ion-select-options appear, you look for the value and re-select it. The binding between the parent ion-select value and the ion-select-options children has to be there all the time of course...
You could also use angular's ViewChildrens maybe to detect changes in the ion-select-options array.
There are many ways to accomplish this, even the mutationobserver way, one of the simplest and most primitive of course, would be better than to have this bug, because i think this is really really urgent. You cannot use (async) forms at all (without the *ngIf workaround, which newcomers maybe dont know)

@Leo1305Pineda
Copy link

Leo1305Pineda commented Dec 6, 2020

This worked for me

<ion-select interface="popover" [(ngModel)]="selected" (ionChange)="selected = $event.detail.value">
        <ion-select-option *ngFor="let item of list" [value]="item.id">{{ item.text }}</ion-select-option>
</ion-select>

@MaximS
Copy link

MaximS commented Jan 9, 2021

Already have faced this issue. The only workaround I've found is setting values async as well.

Let' suppose I have some items1: Observable<Item1[]>, items2: Observable<Item2[]> and so on for the select options.

Then instead of setting values direclty the following trick does the job:

    combineLatest([items1, items2])
      .pipe(
        first(),
        delay(1)  // remove this delay and the last ovservable value is missed
      )
      .subscribe(() => this.yourForm.patchValue(this.yourValue));

See stackblitz
The "trick" is in address-dialog.component.ts

@JamesPoel
Copy link

JamesPoel commented Jan 18, 2021

My extremely hacky solution was, on setting the select's values, to hide the select for a single tick and then immediately show it again. This forces the component to re-render and is practically invisible.

<template>
	<ion-select :value="user.skills"
                          @ionChange="user.skills = $event.target.value"
                          v-if="enabled"
                          multiple>
            <ion-select-option v-for="(skill,i) in skills"
	                           :key="i"
	                           :value="skill.id">
            {{ skill.name }}
            </ion-select-option>
	</ion-select>
</template>

<script>
export default {
	created(){
		this.$api.skills.index()
			.then( skills => this.skills = skills );
	},
        watch: {
          skills() {
            this.enabled = false;
            this.$nextTick(() => this.enabled = true);
          }
        },
	data(){
		return {
			skills:null,
			enabled:true,
		}
	}
}
</script>

@MaximS
Copy link

MaximS commented May 27, 2021

Please, please, fix it!
The proposed workaround with <ion-select *ngIf=...> cannot be used inside angular reactive forms because missing form control breaks all the form's logic like setValue, patchValue, etc.

May be it's not so good design to depend entirely on DOM elements of <ion-select-options>? May be it's better to keep the list as JS object propery?

@keo9ren
Copy link
Author

keo9ren commented Jun 5, 2021

Actually what we did is to wrap ion-select in our own foo-select angular component and implemented the control value accessor on the foo-select. That way you can use foo-select for the reactive form and use *ngIf on the ion-select. It's not pretty, but it did the job for us.

@Turkzilla
Copy link

Has this been resolved? Any suggestions for Ionic 5, Angular 12?

@alperkay
Copy link

alperkay commented Oct 3, 2021

having the same issue on ionic/vue v 5.4.0

@shprink
Copy link

shprink commented Oct 8, 2021

Still having that with:

    "@ionic/angular": "^5.8.3",
    "@angular/core": "~12.1.0",

@joao91carlosGit
Copy link

Same issue

"@ionic/angular": "^5.5.2",
"@angular/core": "~12.0.1",
"@capacitor/core": "^3.2.4",

@jaminm
Copy link

jaminm commented Aug 13, 2022

This worked for me when I put a function call in the placeholder field.
<ion-select v-model="price" :placeholder="showPlaceholderText()"> </ion-select>

@nandreau
Copy link

Same issue
"@angular/core": "~13.3.9", "@capacitor/core": "^3.5.1", "@ionic/angular": "~6.1.6",

@jadurani
Copy link

jadurani commented Nov 5, 2022

Same issue

"@angular/core": "^14.2.7",
"@capacitor/core": "3.5.1",
"@ionic/angular": "^6.1.9",

@hassanrazakhalid
Copy link

hassanrazakhalid commented Nov 27, 2022

Same issue. One solution here was set initial value empty when creating form
departmentId: new FormControl( '', [Validators.required]),

And then when you load data via api then set

this.callService.getDepartmentsList(this.hospitalId)
      .subscribe(({
        next: value => {
          this.departments = value;
          this.formGroup.controls.departmentId.setValue(this.request!.departmentId)
          // this.formGroup.patchValue({
          //   departmentId: this.request!.departmentId
          // })
        }
      }));

My package versions

"@ionic/angular": "^6.1.9",
"@capacitor/core": "4.5.0",
"@angular/core": "^15.0.0",

@liamdebeasi liamdebeasi changed the title bug: ion-select doesn't recognize delayed select options bug: ion-select doesn't recognize delayed or updated select options Dec 13, 2022
@liamdebeasi
Copy link
Contributor

Hi everyone,

I have a dev build with a proposed fix if anyone is interested in testing:

Angular

npm install @ionic/angular@6.5.1-dev.11674568807.1ecd0007

React

npm install @ionic/react@6.5.1-dev.11674568807.1ecd0007 @ionic/react-router@6.5.1-dev.11674568807.1ecd0007

Vue

npm install @ionic/vue@6.5.1-dev.11674568807.1ecd0007 @ionic/vue-router@6.5.1-dev.11674568807.1ecd0007

Stencil/Vanilla JavaScript

npm install @ionic/core@6.5.1-dev.11674568807.1ecd0007

@liamdebeasi
Copy link
Contributor

Thanks for the issue. This has been resolved via #26667, and a fix will be available in an upcoming release of Ionic Framework. Please feel free to continue testing this dev build, and let me know if you have any questions.

@ionitron-bot
Copy link

ionitron-bot bot commented Feb 24, 2023

Thanks for the issue! This issue is being locked to prevent comments that are not relevant to the original issue. If this is still an issue with the latest version of Ionic, please create a new issue and ensure the template is fully filled out.

@ionitron-bot ionitron-bot bot locked and limited conversation to collaborators Feb 24, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
package: core @ionic/core package type: bug a confirmed bug report
Projects
None yet
Development

Successfully merging a pull request may close this issue.