问题
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 theMatSelect
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