我目前正在尝试为Angular Material组件编写包装器/自定义组件,以便它们具有自己的命名空间和一些自定义。我在这里找到了一些人们试图包装单个组件的帖子,我设法对其中的一些组件进行了修改,但是我不确定是否保留了原始组件的100%的功能。
例如,我在this answer之后创建了自己的自动填充组件,并添加了一些缺少的东西。我使用Angular Material Component API作为参考。
这是我的组件当前的外观,其中大部分功能都在其中:
autocomplete.component.html
<mat-form-field>
<input #input matInput [type]="type" class="form-control" [matAutocomplete]="autocomplete" (input)="valueChanged($event)" [readonly]="readonly"
(focus)="$event.target.select()" (blur)="onTouched()">
<mat-autocomplete #autocomplete="matAutocomplete" [autoActiveFirstOption]="autoActiveFirstOption" [disableRipple]="disableRipple" [panelWidth]="panelWidth" [displayWith]="displayFunction" (optionSelected)="onOptionSelected($event)">
<mat-option *ngFor="let option of filteredOptions" [value]="option">
{{ displayFunction(option) }}
</mat-option>
</mat-autocomplete>
</mat-form-field>
autocomplete.component.ts
import { Component, Input, AfterViewInit, ViewChild, OnChanges, SimpleChanges, forwardRef, Injector, Output, EventEmitter } from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor, NgControl, Validator, AbstractControl, NG_VALIDATORS } from '@angular/forms';
import { MatAutocompleteTrigger, MatInput, ErrorStateMatcher } from '@angular/material';
@Component({
selector: 'custom-autocomplete',
templateUrl: './autocomplete.component.html',
styleUrls: ['./autocomplete.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => AutocompleteComponent),
multi: true
},
{
provide: NG_VALIDATORS,
useExisting: forwardRef(() => AutocompleteComponent),
multi: true
}
]
})
export class AutocompleteComponent implements AfterViewInit, OnChanges, ControlValueAccessor, Validator {
// Autocomplete inputs
@Input() autoActiveFirstOption: boolean = true;
@Input() classList: string = '';
@Input() disableRipple: boolean = false;
@Input() panelWidth: string | number = "1rem";
@Input() displayFunction: (value: any) => string = this.defaultDisplayFn;
@Input() filterFunction: (value: any) => any[] = this.defaultFilterFn;
@Input() options: any[] = [];
// Autocomplete outputs
@Output() closed: EventEmitter<void>;
@Output() opened: EventEmitter<void>;
// Input inputs
@Input() readonly = false;
@Input() type: string = "text";
@Input() errorStateMatcher: ErrorStateMatcher;
@ViewChild(MatAutocompleteTrigger) trigger: MatAutocompleteTrigger;
@ViewChild(MatInput) matInput: MatInput;
filteredOptions: any[];
optionSelected = '';
onChange = (val: any) => {};
onTouched = () => {};
constructor(
private injector: Injector
) { }
ngAfterViewInit() {
// enables autocomplete by using tab
this.trigger.panelClosingActions
.subscribe(
e => {
if (this.trigger.activeOption) {
const value = this.trigger.activeOption.value;
this.writeValue(value);
this.onChange(value);
}
}
);
// this is needed in order for the mat-form-field to be marked as invalid when the control is invalid
setTimeout(() => {
this.matInput.ngControl = this.injector.get(NgControl, null);
});
}
ngOnChanges(changes: SimpleChanges) {
if (changes.options) {
this.filterOptions(this.optionSelected);
}
}
writeValue(obj: any): void {
if (obj) {
this.trigger.writeValue(obj);
this.optionSelected = obj;
this.filterOptions(obj);
}
}
registerOnChange(fn: any): void {
this.onChange = fn;
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
}
setDisabledState?(isDisabled: boolean): void {
this.matInput.disabled = isDisabled;
this.trigger.setDisabledState(isDisabled);
}
validate(c: AbstractControl): { [key: string]: any; } {
return { 'custom': true };
}
valueChanged(event) {
const value = event.target.value;
this.optionSelected = value;
this.onChange(value);
this.filterOptions(value);
}
onOptionSelected(event) {
const value = event.option.value;
this.optionSelected = value;
this.onChange(value);
this.filterOptions(value);
}
filterOptions(value) {
this.filteredOptions = this.filterFunction(value);
}
private defaultFilterFn(value) {
let name = value;
if (value && typeof value === 'object') {
name = value.name;
}
return this.options.filter(
o => o.name.toLowerCase().indexOf(name ? name.toLowerCase() : '') !== -1
);
}
defaultDisplayFn(value) {
return value ? value.name : value;
}
}
现在我不确定如何实现其他属性,例如
@Output() closed: EventEmitter<void>;
@Output() opened: EventEmitter<void>;
和
id: string - Unique ID to be used by autocomplete trigger's "aria-owns" property.
isOpen: boolean - Whether the autocomplete panel is open.
panel: ElementRef - Element for the panel containing the autocomplete options.
showPanel: boolean - Whether the autocomplete panel should be visible, depending on option length.
我需要对每个Angular Material组件执行此操作(这在时间/难度上是现实的吗?)。 任何帮助将不胜感激。