Ion-picker in React: modify options with ionPickerColChange

Hi,
I’m trying to use the ion-picker component with multiple columns in a React Ionic app.

I need to set the possible values in one column based on the selected values in the other columns analogous to what the ion-datetime component is doing (where the values for day depend on the selected month and year).

I can catch the change with

document.addEventListener('ionPickerColChange', (event) => {
    ...
    this.setState({ columns: ... })
}

The problem is that the picker (which receives the columns as <IonPicker … columns={this.state.columns} /> doesn’t seem to get refreshed when the state changes. Items that should be disabled are still there and only disappear when I close and reopen the picker.

Any idea how I can solve this? Or alternatively, does someone know an npm package that I can use instead which contains components for a datepicker and a custom picker which supports this functionality?

Thanks!

Same issue with Ionic 5 angular! How did you resolve this behavior? It’s very strange that Ionic Team doesn’t provide a method to refresh picker.

1 Like

Unfortunately I didn’t find a solution…

It seems there are methods to refresh and update the picker column, but these methods are private. :roll_eyes:

1 Like

As a workaround you can mess with the DOM directly, the following Angular code shows only the one option in the second column when the first column value changes to 24.

    picker.addEventListener('ionPickerColChange', async (event: any) => {
      if (event.detail.name === "h") {
        if (event.detail.selectedIndex === 24) {
          this.showFirstOptionOnly(picker);
        } else if (this.onlyFirstOptionVisible) {
          this.showAllOptions(picker);
        }
      }
    });
    const promise = picker.present();
    promise.then(() => {
      const hourColumn = picker.columns[0];
      if (hourColumn.selectedIndex === 24) {
        this.showFirstOptionOnly(picker);
      }
    });
  private showFirstOptionOnly(picker: HTMLIonPickerElement) {
    this.onlyFirstOptionVisible = true;
    const minuteColumn = picker.columns[1];
    minuteColumn.prevSelected = minuteColumn.selectedIndex;
    minuteColumn.selectedIndex = 0;
    const optionElements = ModalPage.provideOptionElements(picker);
    for (let i = 1; i < optionElements.children.length; i++) {
      const htmlElement = optionElements.children[i] as HTMLElement;
      htmlElement.style.display = 'none';
    }
    const firstElement = optionElements.children[0] as HTMLElement;
    this.prevTransform = firstElement.style.transform;
    firstElement.style.transform = "rotateX(0deg) translate3d(0px,0px,90px)";
  }
  private showAllOptions(picker: HTMLIonPickerElement) {
    this.onlyFirstOptionVisible = false;
    const minuteColumn = picker.columns[1];
    minuteColumn.selectedIndex = minuteColumn.prevSelected;
    const optionElements = ModalPage.provideOptionElements(picker);
    for (let i = 1; i < optionElements.children.length; i++) {
      const htmlElement = optionElements.children[i] as HTMLElement;
      htmlElement.style.display = 'block';
    }
    if (this.prevTransform) {
      const firstElement = optionElements.children[0] as HTMLElement;
      firstElement.style.transform = this.prevTransform;
      this.prevTransform = null;
    }
  }
  private static provideOptionElements(picker: HTMLIonPickerElement) {
    return picker.children[2].children[1].children[2].children[0];
  }

You can adjust the code for your bespoke purposes.