选择所有垫子选项并取消选择全部

Posted

技术标签:

【中文标题】选择所有垫子选项并取消选择全部【英文标题】:Select All mat option and deselect All 【发布时间】:2019-01-05 21:14:52 【问题描述】:

我的场景如下:

我要实现的是:

    当用户点击All时,所有选项都会被选中,当用户再次点击All时,所有选项都会被取消。 如果 All 选项被选中并且用户单击除All 之外的任何其他复选框,则应取消选择 All 和单击的复选框。 当用户一一选择 4 个选项时,应选择全部。

html 文件

<mat-select placeholder="User Type" formControlName="UserType" multiple>
    <mat-option *ngFor="let filters of userTypeFilters" [value]="filters.key">
          filters.value
    </mat-option>
        <mat-option #allSelected (click)="toggleAllSelection()" [value]="0">All</mat-option>
</mat-select>

TS 文件

this.searchUserForm = this.fb.group(
  userType: new FormControl('')
);

userTypeFilters = [
  
    key: 1, value: 'Value 1',
  ,
  
    key: 2, value: 'Value 2',
  ,
  
    key: 3, value: 'Value 3',
  ,
  
    key: 4, value: 'Value 4',
  
]

toggleAllSelection() 
  if (this.allSelected.selected) 
    this.searchUserForm.controls.userType
    .patchValue([...this.userTypeFilters.map(item => item.key), 0]);
   else 
    this.searchUserForm.controls.userType.patchValue([]);
  

现在,如何实现第二点和第三点

Stackblitz 是:https://stackblitz.com/edit/angular-material-with-angular-v5-znfehg?file=app/app.component.html

【问题讨论】:

我看到第二个 senario 作品对吗? no.. 只有第一个有效.. 第二个选项是,如果选择了 All,然后用户取消选择任何选项,则应取消该选项和 All 选项 【参考方案1】:

使用如下代码在点击每个mat-optionselect()/deselect()所有选项时创建函数:

见 stackblitz:https://stackblitz.com/edit/angular-material-with-angular-v5-jsgvx6?file=app/app.component.html

TS:

togglePerOne(all) 
   if (this.allSelected.selected)   
    this.allSelected.deselect();
    return false;

  if(this.searchUserForm.controls.userType.value.length==this.userTypeFilters.length)
    this.allSelected.select();


  toggleAllSelection() 
    if (this.allSelected.selected) 
      this.searchUserForm.controls.userType
        .patchValue([...this.userTypeFilters.map(item => item.key), 0]);
     else 
      this.searchUserForm.controls.userType.patchValue([]);
    
  

HTML:

<form [formGroup]="searchUserForm" fxFlex fxLayout="column" autocomplete="off" style="margin: 30px">
    <mat-select placeholder="User Type" formControlName="userType" multiple>
        <mat-option *ngFor="let filters of userTypeFilters" [value]="filters.key" (click)="togglePerOne(allSelected.viewValue)">
            filters.value
        </mat-option>
        <mat-option #allSelected (click)="toggleAllSelection()" [value]="0">All</mat-option>
    </mat-select>
</form>

【讨论】:

如果我想避免在所选项目列表中看到“全部”,我该怎么办? 对不起,我可能没有解释清楚,我想避免它出现在所选项目列表中,但我不想删除相应的复选框【参考方案2】:

另一种方法是使用 @ViewChild 选择器来获取 mat-select 组件并调整 mat-options 项的选中或未选中。我们还需要一个变量来保存选择的实际状态,以便在每次点击时选择或取消选择所有元素。希望会有所帮助。

  import MatOption, MatSelect from "@angular/material";
  
  export class ExampleAllSelector 
  
    myFormControl = new FormControl();
    elements: any[] = [];

    allSelected = false;

    @ViewChild('mySel') skillSel: MatSelect;

    constructor() 

    toggleAllSelection() 
      this.allSelected = !this.allSelected;  // to control select-unselect
      
      if (this.allSelected) 
        this.skillSel.options.forEach( (item : MatOption) => item.select());
       else 
        this.skillSel.options.forEach( (item : MatOption) => item.deselect());
      
      this.skillSel.close();
    
  
      <mat-select #mySel placeholder="Example" [formControl]="myFormControl" multiple>
        <mat-option [value]="0" (click)="toggleAllSelection()">All items</mat-option>
        <mat-option *ngFor="let element of elements" [value]="element">skill.name</mat-option>
      </mat-select>

【讨论】:

skillAllSelected 应该是什么? @Takatalvi 应该是“allSelected” 谢谢,在选择循环的情况下,这对我有用,我没有使用 viewChild,而是使用了 @ViewChildren('mySel') SkillSel: QueryList;并且要访问选项应该类似于 const select = this.skillSel.toArray()[index];【参考方案3】:

这是一个如何扩展材质选项组件的示例。

见 stackblitz Demo

组件:

import  ChangeDetectorRef, Component, ElementRef, HostListener, HostBinding, Inject, Input, OnDestroy, OnInit, Optional  from '@angular/core';
import  MAT_OPTION_PARENT_COMPONENT, MatOptgroup, MatOption, MatOptionParentComponent  from '@angular/material/core';
import  AbstractControl  from '@angular/forms';
import  MatPseudoCheckboxState  from '@angular/material/core/selection/pseudo-checkbox/pseudo-checkbox';
import  Subject  from 'rxjs';
import  takeUntil  from 'rxjs/operators';

@Component(
  selector: 'app-select-all-option',
  templateUrl: './select-all-option.component.html',
  styleUrls: ['./select-all-option.component.css']
)
export class SelectAllOptionComponent extends MatOption implements OnInit, OnDestroy 
  protected unsubscribe: Subject<any>;

  @Input() control: AbstractControl;
  @Input() title: string;
  @Input() values: any[] = [];

  @HostBinding('class') cssClass = 'mat-option';

  @HostListener('click') toggleSelection(): void 
    this. _selectViaInteraction();

    this.control.setValue(this.selected ? this.values : []);
  

  constructor(elementRef: ElementRef<HTMLElement>,
              changeDetectorRef: ChangeDetectorRef,
              @Optional() @Inject(MAT_OPTION_PARENT_COMPONENT) parent: MatOptionParentComponent,
              @Optional() group: MatOptgroup) 
    super(elementRef, changeDetectorRef, parent, group);

    this.title = 'Select All';
  

  ngOnInit(): void 
    this.unsubscribe = new Subject<any>();

    this.refresh();

    this.control.valueChanges
      .pipe(takeUntil(this.unsubscribe))
      .subscribe(() => 
        this.refresh();
      );
  

  ngOnDestroy(): void 
    super.ngOnDestroy();

    this.unsubscribe.next();
    this.unsubscribe.complete();
  

  get selectedItemsCount(): number 
    return this.control && Array.isArray(this.control.value) ? this.control.value.filter(el => el !== null).length : 0;
  

  get selectedAll(): boolean 
    return this.selectedItemsCount === this.values.length;
  

  get selectedPartially(): boolean 
    const selectedItemsCount = this.selectedItemsCount;

    return selectedItemsCount > 0 && selectedItemsCount < this.values.length;
  

  get checkboxState(): MatPseudoCheckboxState 
    let state: MatPseudoCheckboxState = 'unchecked';

    if (this.selectedAll) 
      state = 'checked';
     else if (this.selectedPartially) 
      state = 'indeterminate';
    

    return state;
  

  refresh(): void 
    if (this.selectedItemsCount > 0) 
      this.select();
     else 
      this.deselect();
    
  

HTML:

<mat-pseudo-checkbox class="mat-option-pseudo-checkbox"
                     [state]="checkboxState"
                     [disabled]="disabled"
                     [ngClass]="selected ? 'bg-accent': ''">
</mat-pseudo-checkbox>

<span class="mat-option-text">
  title
</span>

<div class="mat-option-ripple" mat-ripple
     [matRippleTrigger]="_getHostElement()"
     [matRippleDisabled]="disabled || disableRipple">
</div>

CSS:

.bg-accent 
  background-color: #2196f3 !important;

【讨论】:

我喜欢这个解决方案:有没有办法让这个键盘变得友好?看起来自定义选项被 MatSelect 组件完全忽略了.. 在 Angular 10 上,我不得不删除调用 _selectViaInteraction 因为全选选项没有改变状态。【参考方案4】:

只需添加一个复选框,无需即可为您的数据源添加新选项。

请参阅:Demo

import  Component, VERSION, ViewChild  from '@angular/core';
import  FormControl  from '@angular/forms';
import  MatSelect  from '@angular/material/select';
import  MatOption  from '@angular/material/core';

@Component(
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
)
export class AppComponent  
  @ViewChild('select') select: MatSelect;

  allSelected=false;
   foods: any[] = [
    value: 'steak-0', viewValue: 'Steak',
    value: 'pizza-1', viewValue: 'Pizza',
    value: 'tacos-2', viewValue: 'Tacos'
  ];
  toggleAllSelection() 
    if (this.allSelected) 
      this.select.options.forEach((item: MatOption) => item.select());
     else 
      this.select.options.forEach((item: MatOption) => item.deselect());
    
  
   optionClick() 
    let newStatus = true;
    this.select.options.forEach((item: MatOption) => 
      if (!item.selected) 
        newStatus = false;
      
    );
    this.allSelected = newStatus;
  
.select-all
  margin: 5px 17px;
 
<mat-form-field>
  <mat-label>Favorite food</mat-label>
  <mat-select  #select multiple>
    <div class="select-all">
        <mat-checkbox [(ngModel)]="allSelected"
                        [ngModelOptions]="standalone: true"
                        (change)="toggleAllSelection()">Select All</mat-checkbox>
    </div>
    <mat-option (click)="optionClick()" *ngFor="let food of foods" [value]="food.value">
      food.viewValue
    </mat-option>
  </mat-select>
</mat-form-field>

【讨论】:

这应该是公认的答案,因为它不会在值中添加不必要的额外项目。 我分叉了你的演示。相反,我使用一个按钮完成了一个复选框。将其封装在自定义组件中,查看:stackblitz.com/edit/angular-material-select-all-button【参考方案5】:

另一种可能的解决方案:

在模板中使用&lt;mat-select [(value)]="selectedValues",并在组件中通过切换功能设置selectedValues

工作Stackblitz 演示。

组件

export class AppComponent 

  selectedValues: any;
  allSelected = false;

   public displayDashboardValues = [
    key:'0', valuePositionType: 'undefined', viewValue:'Select all',
    key:'1', valuePositionType: 'profit-loss-area', viewValue:'result',
    key:'2', valuePositionType: 'cash-area', viewValue:'cash',
    key:'3', valuePositionType: 'balance-area', viewValue:'balance',
    key:'4', valuePositionType: 'staff-area' ,viewValue:'staff',
    key:'5', valuePositionType: 'divisions-area', viewValue:'divisions',
    key:'6', valuePositionType: 'commisions-area', viewValue:'commisions',    
  ];

  toggleAllSelection() 
      this.allSelected = !this.allSelected;
      this.selectedValues = this.allSelected ? this.displayDashboardValues : [];
    

模板

        <mat-select  [(value)]="selectedValues" (selectionChange)="selectionChange($event)" formControlName="dashboardValue" multiple>
          <mat-option [value]="displayDashboardValues[0]" (click)="toggleAllSelection()"> displayDashboardValues[0].viewValue </mat-option>
          <mat-divider></mat-divider>
          <div *ngFor="let dashboardPosition of displayDashboardValues">
            <mat-option class="dashboard-select-option" *ngIf="dashboardPosition.key>0" [value]="dashboardPosition">
               dashboardPosition.viewValue 
            </mat-option>
          </div>
        </mat-select>

【讨论】:

当您使用箭头键/单击全选时的“输入”键时会变得很奇怪【参考方案6】:

其他答案存在一些问题。最重要的是他们正在监听未完成的点击事件(用户可以通过键盘上的空格键选择一个选项)。

我创建了一个可以解决所有问题的组件:

@Component(
  selector: 'app-multi-select',
  templateUrl: './multi-select.component.html',
  styleUrls: ['./multi-select.component.scss'],
)
export class MultiSelectComponent<V> implements OnInit 
  readonly _ALL_SELECTED = '__ALL_SELECTED__' as const;
  @Input() options: ReadonlyArray< value: V; name: string > = [];
  @Input('selectControl') _selectControl!: FormControl | AbstractControl | null | undefined;
  get selectControl(): FormControl 
    return this._selectControl as FormControl;
  
  @Input() label: string = '';
  @Input() hasSelectAllOption = false;
  selectedValues: (V | '__ALL_SELECTED__')[] = [];

  constructor() 

  ngOnInit(): void 

  onSelectAllOptions( isUserInput, source:  selected  : MatOptionSelectionChange) 
    if (!isUserInput) return;
    this.setValues(selected ? this.options.map(o => o.value) : []);
  

  private setValues(values: (V | '__ALL_SELECTED__')[]) 
    const hasAllOptions = ArrayUtils.arraysAreSame(
      values,
      this.options.map(o => o.value),
    );
    if (!values.includes(this._ALL_SELECTED)) 
      if (hasAllOptions) 
        values = [...values, this._ALL_SELECTED];
      
     else if (!hasAllOptions) 
      values = values.filter(o => o !== this._ALL_SELECTED);
    

    setTimeout(() => 
      this.selectedValues = values;
    );
    this.selectControl.setValue(values.filter(o => (o as any) !== this._ALL_SELECTED));
  

  onSelectOtherOptions( isUserInput, source:  selected, value  : MatOptionSelectionChange) 
    if (!isUserInput) return;
    this.setValues(
      selected ? [...this.selectedValues, value] : this.selectedValues.filter(o => o !== value),
    );
  



<mat-form-field>
  <mat-label>Choose some options</mat-label>
  <mat-select multiple [value]="selectedValues">
    <mat-option
      *ngFor="let d of options"
      [value]="d.value"
      (onSelectionChange)="onSelectOtherOptions($event)"
    >
       d.name 
    </mat-option>
    <mat-option
      *ngIf="hasSelectAllOption"
      [value]="_ALL_SELECTED"
      (onSelectionChange)="onSelectAllOptions($event)"
    >
      Select all
    </mat-option>
  </mat-select>
</mat-form-field>

【讨论】:

能否分享完整的 stackblitz 示例?谢谢

以上是关于选择所有垫子选项并取消选择全部的主要内容,如果未能解决你的问题,请参考以下文章

如何获得选择的垫子选项的价值?

SumoSelect 取消选择其他选项

复制所有文本后隐藏复制和取消选择 UITextView 选项

取消选择单选选项

如何选择下拉菜单中的所有选项 - Selenium Webdriver?

在 Bootstrap 选择器上使用 jQuery 取消选择选项