问题
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
_resultsarray on theMatSelectbut 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