Как добавить опцию «Выбрать все» в ion-select в ionic 5

#angular #ionic-framework #ionic5

#angular #ionic-framework #ionic5

Вопрос:

Я попытался добавить опцию выбора ion, а затем попытался обработать логику выбора всех в событии изменения, но событие изменения вообще не срабатывает. кажется, он не поддерживает события

     <ion-item>
        <ion-label>Test</ion-label>
        <ion-select [(ngModel)]="selectedValues" multiple="true">
            <ion-select-option (ionChange)="selectAll()">Select All</ion-select-option>
            <ion-select-option [value]="option" *ngFor="let option of  items">{{option}}
            </ion-select-option>
        </ion-select>
    </ion-item>
  

пример https://stackblitz.com/edit/ionic-5-angular-10-start-template-hure6j?file=src/app/tabs/tabs.page.html

Ответ №1:

В Ionic с несколькими вариантами выбора нет встроенного способа «выбрать все» или «выбрать ни одного», но я придумал пользовательское решение, которое делает именно это. Вам нужно будет использовать пользовательский компонент оповещения вместо an <ion-select> , и вам нужно будет вызвать оповещение программно.

 constructor(
    private alertController: AlertController,
    public platform: Platform
  ) {}

  async showAlert() {

    let buttons = [
      {
        text: 'All',
        cssClass: 'all-none-button',
        handler: () => {

          // check all checkboxes
          alert.inputs = alert.inputs.map((checkbox) => {
            checkbox.checked = true;
            return checkbox;
          });

          return false;
        }
      }, {
        text: 'None',
        cssClass: 'all-none-button',
        handler: () => {

          // uncheck all checkboxes
          alert.inputs = alert.inputs.map((checkbox) => {
            checkbox.checked = false;
            return checkbox;
          });

          return false;
        }
      }, {
        text: 'OK',
        handler: (data) => {
          // handle the data returned from the alert here
          console.log(data);
        }
      }, {
        text: 'Cancel',
        role: 'cancel',
      }
    ];

    // adjust button order in four button layout for ios
    if (this.platform.is('ios')) {
      const okButton = { ...buttons[2] };
      const cancelButton = { ...buttons[3] };
      buttons = [buttons[0], buttons[1], cancelButton, okButton];
    }

    const alert = await this.alertController.create({
      header: 'Select Option',
      inputs: [
        {
          label: 'Option 1',
          type: 'checkbox',
          value: 'one',
          checked: false
        },
        {
          label: 'Option 2',
          type: 'checkbox',
          value: 'two',
          checked: false
        },
        {
          label: 'Option 3',
          type: 'checkbox',
          value: 'three',
          checked: false
        },
      ],
      cssClass: 'four-button-alert',
      buttons: [...buttons]
    });

    await alert.present();
  }
  

И вам также понадобится немного пользовательского CSS, чтобы получить макет из четырех кнопок для этого пользовательского оповещения.

 .four-button-alert.md {
    .alert-button-group-vertical {
        display: block;

        button:nth-of-type(1),
        button:nth-of-type(2) {
            float: left;
            color: var(--ion-color-medium);
        }

        button:nth-of-type(3),
        button:nth-of-type(4) {
            float: right;
        }
    }
}

.four-button-alert.ios {
    .alert-button-group-vertical {
        flex-direction: row !important;

        button:nth-of-type(1) {
            color: var(--ion-color-medium);
        }

        button:nth-of-type(2) {
            color: var(--ion-color-medium);
            border-right: none;
        }
    }
}
  

Готовый продукт выглядит следующим образом (iOS и Android).

введите описание изображения здесь

Ответ №2:

Я согласен с Уэсли в том, что, поскольку ion-select использует контроллер оповещений или всплывающее окно под капотом, лучшим способом может быть использование пользовательского оповещения. Но если вам нужно сохранить «внешний вид» ion-select, вы можете сделать следующее:

HTML:

 <ion-item (click)="openSelector(selector)">
      <ion-label>Pizza Toppings</ion-label>
      <ion-select #selector style="pointer-events: none" [interfaceOptions]="customAlertOptions" multiple="true" [value]="basket">
      <ion-select-option *ngFor="let option of options" [value]="option">{{option}}</ion-select-option>
    </ion-select>
</ion-item>
  

TS:

 import { Inject, Component, Renderer2 } from '@angular/core';
import { DOCUMENT } from '@angular/common';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {

  options = ["cheese", "pepperoni", "basil"];
  basket = [];

  listener;
  selectAllCheckBox: any;
  checkBoxes: HTMLCollection;

  customAlertOptions: any = {
    header: 'Pizza Toppings',
    subHeader: 'Select All:',
    message: '<ion-checkbox id="selectAllCheckBox"></ion-checkbox>'
  };

  constructor(@Inject(DOCUMENT) private document: Document, private renderer: Renderer2) {}

  openSelector(selector) {
    selector.open().then((alert)=>{
      this.selectAllCheckBox = this.document.getElementById("selectAllCheckBox");
      this.checkBoxes = this.document.getElementsByClassName("alert-checkbox");
      this.listener = this.renderer.listen(this.selectAllCheckBox, 'click', () => {
          if (this.selectAllCheckBox.checked) {
            for (let checkbox of this.checkBoxes) {
              if (checkbox.getAttribute("aria-checked")==="false") {
                (checkbox as HTMLButtonElement).click();
              };
            };
          } else {
            for (let checkbox of this.checkBoxes) {
              if (checkbox.getAttribute("aria-checked")==="true") {
                (checkbox as HTMLButtonElement).click();
              };
            };
          }
      });
      alert.onWillDismiss().then(()=>{
        this.listener();
      });
    })
  }
  
}
  

По сути, вы отключаете обработчик щелчков по умолчанию в ion-select и передаете ссылку «селектор» через событие щелчка, связанное с ion-элементом. Затем вы используете метод ‘open’ ion-select, чтобы получить доступ к экземпляру ‘alert’.
Поскольку свойство ‘message’ параметров оповещения поддерживает «очищенный» HTML, вы все равно можете передать ему HTML-элемент (флажок) с его идентификатором. Затем, используя ссылку на документ, вы устанавливаете флажок и добавляете прослушиватель (позже он будет удален на крючке willDismiss).
Внутри метода click вы можете реализовать функциональность выбора all / none.

Демо-версия: https://stackblitz.com/edit/ionic-angular-v5-2jqnsv?file=src/app/app.component.ts

Комментарии:

1. Спасибо, это решило мою проблему, но я хочу снять флажок selectAll, когда какой-либо из элементов не отмечен. Любой намек на это

Ответ №3:

Обновление июль-2021: добавлен поиск и перенесен выбор всех и поиск в пользовательские элементы для удобства управления

Пример кода :
https://stackblitz.com/edit/ionic-5-angular-10-start-template-6m73vb
введите описание изображения здесь

Предыдущий подход:

В итоге я использовал решение @Sergey Rudenko и создал директиву с некоторыми дополнительными улучшениями и обработкой select all в большем количестве сценариев. Если кто-либо заинтересован, пожалуйста, найдите соответствующий код ниже

Решение 1: Выберите директиву All Checkbox. На основе ответа @Sergey Rudenko

 import { ContentChild, Directive, HostListener, Renderer2 } from '@angular/core';
import { IonSelect } from '@ionic/angular';
@Directive({
    selector: '[selectAllDirective]'
})
export class SelectAllDirective {
    @ContentChild(IonSelect) ionSelect;
    constructor(private renderer: Renderer2) { }

    ngAfterViewInit() {
        this.ionSelect.el.style.pointerEvents = "none";
    }
    @HostListener('click', ['$event'])
    onClick() {
        this.ionSelect.open().then(alert => {
            let id = "selectAllCheckBox";
            alert.message = `<ion-checkbox id="${id}"></ion-checkbox><ion-label class="m-l-15">Select All</ion-label>`;

            setTimeout(() => {
                let selectAll: any = alert.querySelector("#"   id);
                let checkboxes = Array.from(alert.querySelectorAll(".alert-checkbox"));
                let setState = () => {
                    let isAllChecked = checkboxes.every((c: any) => c.ariaChecked === "true");
                    let isAllUnChecked = checkboxes.every((c: any) => c.ariaChecked === "false");
                    if (isAllChecked || isAllUnChecked) {
                        selectAll.indeterminate = false;
                        selectAll.checked = isAllChecked;
                    } else {
                        selectAll.indeterminate = true;
                    }
                }
                setState();

                let checkboxListeners = checkboxes.map(ci => this.renderer.listen(ci, 'click', () => {
                    setTimeout(setState);
                }));

                let selectAllListener = this.renderer.listen(selectAll, 'click', (event) => {
                    setTimeout(() => alert.inputs = alert.inputs.map(i => { i.checked = event.target.checked; return i; }));
                });
                alert.onWillDismiss().then(() => {
                    selectAllListener();
                    checkboxListeners.forEach(i => i());
                });
            });
        });
    }
}
  

Решение 2: выберите все с помощью кнопок, используя директиву. На основе ответа @Wesley

 import { ContentChild, Directive, HostListener, Renderer2 } from '@angular/core';
import { IonSelect } from '@ionic/angular';
@Directive({
    selector: '[selectAllWithButtonDirective]'
})
export class SelectAllWithButtonDirective {
    @ContentChild(IonSelect) ionSelect;
    constructor(private renderer: Renderer2) { }

    ngAfterViewInit() {
        this.ionSelect.el.style.pointerEvents = "none";
    }
    @HostListener('click', ['$event'])
    onClick() {
        this.ionSelect.open().then(alert => {
            alert.cssClass = 'unset-vertical-buttons';
            alert.buttons = [{
                text: 'All',
                handler: () => {
                    alert.inputs = alert.inputs.map((checkbox) => {
                        checkbox.checked = true;
                        return checkbox;
                    });
                    return false;
                }
            }, {
                text: 'None',
                handler: () => {
                    alert.inputs = alert.inputs.map((checkbox) => {
                        checkbox.checked = false;
                        return checkbox;
                    });
                    return false;
                }
            }, ...alert.buttons,];
        });
    }
}
  

styles.css

 .unset-vertical-buttons .alert-button-group-vertical {
    flex-direction: row !important;
}
.m-l-15 {
  margin-left: 15px;
}
  

пример кода обеих директив: https://stackblitz.com/edit/ionic-5-angular-10-start-template-hu3y3u?file=src/app/tabs/select-all-with-button-directive.ts