如何在 ionic 5 中向 ion-select 添加 select all 选项

How to add select all option to ion-select in ionic 5

我尝试添加 ion-select-选项,然后尝试处理 select 更改事件中的所有逻辑,但更改事件根本没有触发。好像不支持事件

    <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

使用 Ionic 多项选择 selects,没有内置的方法来“select 全部”或“select none”,但我想出了一个定制解决方案就是这样做的。您将需要使用自定义 alert component 而不是 <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)。

我同意 Wesley 的观点,因为 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-item 绑定点击事件传递 'selector' 引用。然后你使用 ion-select 的 'open' 方法来访问 'alert' 的实例。 由于警报选项的 'message' 属性 支持“已清理” HTML,您仍然可以将 HTML 元素及其 ID 传递给它(复选框)。然后使用文档参考,您可以获得复选框并添加侦听器(稍后将在 willDismiss 挂钩上删除)。 在 click 方法中你可以实现 select all / none.

的功能

演示:https://stackblitz.com/edit/ionic-angular-v5-2jqnsv?file=src/app/app.component.ts

2021 年 7 月更新: 添加搜索并将 select 全部和搜索移动到自定义元素以便于管理

代码示例: https://stackblitz.com/edit/ionic-5-angular-10-start-template-6m73vb

以前的方法:

我最终使用@Sergey Rudenko 解决方案并创建了指令,并在更多场景中进行了一些改进和处理 select。有兴趣的朋友请在下面找到相关代码

解决方案 1:Select 所有复选框指令。基于@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:Select 所有按钮都使用指令。基于@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