angular add empty mat-option to all not required mat-select in my app

橙三吉。 提交于 2020-12-31 10:36:59

问题


How can I add dynamically empty mat-option to all not required mat-select in my app.

like :

 <mat-form-field appearance="outline">
         <mat-label>HMO</mat-label>
         <mat-select>
              <--how can I add this dynamically-->
              <mat-option>--</mat-option>
              <mat-option *ngFor="let hmo of HMOList"
                              [value]="hmo.Code">
                    {{ hmo.Desc }}
              </mat-option>
         </mat-select>
  </mat-form-field>

any idea?


回答1:


I used the response from Prince, but my model didn't update. And I added the following in OptionalMatSelectDirective

import { Directive, HostListener, OnInit, Renderer2, ElementRef, AfterViewInit } from '@angular/core';
import { MatSelect } from '@angular/material/select';

@Directive({
  selector: 'mat-select'
})
export class OptionalMatSelectDirective {

  constructor(private matSelect: MatSelect, private elementRef: ElementRef,
              private renderer: Renderer2) { }

  @HostListener('openedChange', ['$event'])
  onOpenedChange( isOpened : boolean)
  {

    if(!isOpened || this.matSelect.required)
    {
      return;
    }
    const matOption = this.renderer.createElement('mat-option');
    this.renderer.setAttribute(matOption, 'class', 'mat-option');
    this.renderer.listen(matOption,'click', ()=>
    {
      this.matSelect.value = '';
      **this.matSelect.ngControl.viewToModelUpdate(undefined);**
      this.matSelect.close();
    });

    const panel= document.querySelector('.mat-select-panel');
    if(!panel)
    {
      throw new Error('Cannot find mat select panel');
    }
    this.renderer.insertBefore(panel,matOption, panel.firstChild);


  }
}

And, it worked for me!




回答2:


Here is a stackblitz example, based on the various answers here.

The directive below is applied to mat-select, so you do not have to add any extra attributes. It uses the required attribute on the select to know whether to add any blank option when the dropdown opens.

Note: You cannot easily add items to the underlying list of mat options used by the mat-select component, so the mat options elements are added manually, and events need to be bound manually too (I did it for the click event);

import { Directive, HostListener, OnInit, Renderer2, ElementRef, AfterViewInit } from '@angular/core';
import { MatSelect } from '@angular/material/select';

import { Directive, HostListener, OnInit, Renderer2, ElementRef, AfterViewInit } from '@angular/core';
import { MatSelect } from '@angular/material/select';

@Directive({
  selector: 'mat-select'
})
export class OptionalMatSelectDirective {

  constructor(private matSelect: MatSelect, private elementRef: ElementRef,
  private renderer: Renderer2) { }

    @HostListener('openedChange', ['$event'])
    onOpenedChange( isOpened : boolean) 
    {

      if(!isOpened || this.matSelect.required)
      {
        return; //Closing panel, or select is required
      }
      //Manually create a mat option DOM element
      let matOption = this.renderer.createElement("mat-option");
      this.renderer.setAttribute(matOption, 'class', 'mat-option');

      //Bind events to the new mat option
      this.renderer.listen(matOption,'click', ()=>
      {
        this.matSelect.value = "";
        this.matSelect.close();
      });

      //Try to add the new mat option in first position of the list
      let panel= document.querySelector('.mat-select-panel');
      if(!panel)
      {
        throw "Cannot find mat select panel";
      }
      this.renderer.insertBefore(panel,matOption, panel.firstChild);


  }
}



回答3:


Why don't just modify HMOList behind the scene like HMOList = [ ...HMOList, { code: 'None', Desc: ''} ] (or other way around as You want the default option first)? It seems a little bit hacky but it is also seems to be the easiest one. Or better store additional array for mat-select if we want to keep the original array as we copy everything over either way to make sure UI and whatever else will be updated.




回答4:


There are multiple ways to accomplish this task,

First approach first is you can create a property in component class and control the visibility of default mat-option. And bind this property in template like below:

Template

  <mat-select [required]="isRequired">
    <mat-option *ngIf="isRequired">---</mat-option>
    <mat-option *ngFor="let food of foods" [value]="food.value">
      {{food.viewValue}}
    </mat-option>
  </mat-select>

Component class

export class SelectOverviewExample {
  isRequired = true;
  foods: Food[] = [
    {value: 'steak-0', viewValue: 'Steak'},
    {value: 'pizza-1', viewValue: 'Pizza'},
    {value: 'tacos-2', viewValue: 'Tacos'}
  ];
}

Please use the following working link - https://stackblitz.com/edit/angular-azkmrp

Second approach Second approach is you can use template reference variable in template only and use it to add default mat-option. Here I have created a #matselect template reference variable and added the required attribute. Using this template reference variable with the default mat-option. Just toggle the required attribute to test.

 <mat-select #matselect required>
    <mat-option *ngIf="matselect.required">---</mat-option>
    <mat-option *ngFor="let food of foods" [value]="food.value">
      {{food.viewValue}}
    </mat-option>
  </mat-select>

Please find working code here: https://stackblitz.com/edit/angular-zpvjf2




回答5:


You can use javascript unshift function to add first value for all required select elements.

HMOList.unshift( {code:'', desc:'--'});

If you want to add same kind of option in every select box. I have made changes in stackblitz.

See here




回答6:


I can get you this far:

  • Create a new directive with selector as mat-select
  • Get a reference to the MatSelect element via ContentChildren
  • I could use this to add an option to the _results array on the MatSelect but it doesn't update the DOM, so you need to figure that one out

This does not require any change to HTML.

Below is as far as I could get in a short amount of time. There will be a better way to add an option to the MatSelect - you basically need to look at the Angular source files and see how they add a MatOption and then replicate this in your Directive.

Good luck!

@Directive({
  selector: 'mat-select',
  exportAs: 'myMatSelect'
})
export class MyMatSelectDirective {

  constructor(
    public changeDetectorRef: ChangeDetectorRef
  ) { }

  select: MatSelect;

  @ContentChildren(MatSelect) set list(select: QueryList<MatSelect>) {
    this.select = select.first;

    // One possible option is to subscribe to the openChange event
    this.select.openedChange.subscribe(open => {
      let opt = document.createElement("mat-option");
      opt.innerHTML = "--";
      (<any>this.select.options)._results.unshift(new MatOption(new ElementRef(opt), this.changeDetectorRef, this.select, null));
      console.log(this.select)
    })
  }
}



来源:https://stackoverflow.com/questions/60949388/angular-add-empty-mat-option-to-all-not-required-mat-select-in-my-app

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!