AngularMatFormControl、ControlValueAccessor
Angular MatFormControl, ControlValueAccessor
我有一个自定义组件,它是 mat-select 的包装。
我如何在其他组件中使用它?我正在阅读有关 formControl 和 controlValueAccessor 的内容,但我不太了解。
我的custom.html
<mat-select>
<mat-option [value]="opt" *ngFor="let op of opt; index as i">{{opt[i]}}</mat-option>
</mat-select>
<input matInput>
我的custom.ts
import { Component, OnInit, Input } from '@angular/core';
import { MatFormFieldControl } from '@angular/material/form-field';
@Component({
selector: 'cust-input',
templateUrl: './input.component.html',
styleUrls: ['./input.component.scss'],
providers: [{provide: MatFormFieldControl, useExisting: InputComponent}]
})
export class InputComponent extends MatFormFieldControl<string> implements OnInit {
@Input() opt: string;
setDescribedByIds(ids: string[]): void {
throw new Error('Method not implemented.');
}
onContainerClick(event: MouseEvent): void {
throw new Error('Method not implemented.');
}
constructor() {
super();
}
ngOnInit(): void {
}
}
我的app.html
<mat-form-field>
<cust-input [opt]="myopt"></cust-input>
</mat-form-field>
我的app.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
title = 'form';
myopt = ['a', 'b', 'c', 'd'];
}
我不需要像
我的 custmo.html
<mat-form-field>
<mat-select>
<mat-option [value]="opt" *ngFor="let op of opt; index as i">{{opt[i]}}</mat-option>
</mat-select>
<input matInput>
</mat-form-field>
所以答案是:
如果我们想使用我们的自定义 wrappe,我们需要为它创建一个自定义的表单字段控件,
在我们的例子中,我们需要创建自定义 mat-form-field-control
//我们自定义的表单域控件
import { ValueAccessor } from './value-accessor';
import { MatFormFieldControl } from '@angular/material/form-field';
import {
Input,
HostBinding,
Optional,
Self,
ElementRef,
OnDestroy,
Directive,
} from '@angular/core';
import { FocusMonitor } from '@angular/cdk/a11y';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { NgControl } from '@angular/forms';
import { Subject } from 'rxjs';
@Directive()
export class FormControlDirective<T> extends ValueAccessor<T>
implements MatFormFieldControl<T>, OnDestroy {
@Input()
get value() {
return this._value;
}
set value(val: T) {
if (val !== this._value) {
this._value = val;
this.stateChanges.next();
}
}
@Input()
get placeholder() {
return this._placeholder;
}
set placeholder(plc: string) {
this._placeholder = plc;
this.stateChanges.next();
}
@Input()
get required() {
return this._required;
}
set required(req: boolean) {
this._required = coerceBooleanProperty(req);
this.stateChanges.next();
}
get empty() {
return !this._value;
}
constructor(
@Optional() @Self() public ngControl: NgControl,
private fM: FocusMonitor,
private elRef: ElementRef<HTMLElement>
) {
super();
if (this.ngControl != null) {
this.ngControl.valueAccessor = this;
}
fM.monitor(elRef.nativeElement, true).subscribe((origin) => {
this.focused = !!origin;
this.stateChanges.next();
});
}
@HostBinding('class.floating')
get shouldLabelFloat() {
return this.focused || !this.empty;
}
private _value: T | null;
private _placeholder: string;
private _required = false;
nextId = 0;
stateChanges: Subject<void> = new Subject<void>();
focused = false;
@HostBinding() id = `${this.nextId++}`;
errorState = false;
controlType = 'my-select';
autofilled?: boolean;
@HostBinding('attr.aria-desribedby') describedBy = '';
setDescribedByIds(ids: string[]): void {
this.describedBy = ids.join('');
}
onContainerClick(event: MouseEvent): void {
if ((event.target as Element).tagName.toLowerCase() === 'input') {
this.elRef.nativeElement.focus();
}
}
ngOnDestroy(): void {
this.fM.stopMonitoring(this.elRef.nativeElement);
this.stateChanges.complete();
}
}
但是,如果我们想在我们的包装器中使用 ngModel,我们还需要创建我们的自定义控件值访问器
客户价值配件
import { ControlValueAccessor } from '@angular/forms';
export class ValueAccessor<T> implements ControlValueAccessor {
value: T | null;
onChange: Function;
onTouched: Function;
disabled: boolean;
writeValue(val: T): void {
this.value = val;
}
registerOnChange(fn: Function): void {
this.onChange = fn;
}
registerOnTouched(fn: Function): void {
this.onTouched = fn;
}
setDisabledState?(isDisabled: boolean): void {
this.disabled = isDisabled;
}
}
在我们的 custom.wrapper.component.ts 中,我们需要扩展我们的表单控件,
另外,查看custom-form-field-control,并查看constructor,有一个ngControl,我们将其添加到那里,以便一次使用form-control和value-accessor。
select.component.ts
import { Component, Input, OnInit } from '@angular/core';
import { MatFormFieldControl } from '@angular/material/form-field';
import { FormControlDirective } from 'src/app/forms/form-control';
@Component({
selector: 'app-select',
templateUrl: './select.component.html',
styleUrls: ['./select.component.scss'],
providers: [{ provide: MatFormFieldControl, useExisting: SelectComponent }],
})
export class SelectComponent extends FormControlDirective<string>
implements OnInit {
@Input() option: string;
ngOnInit() {}
}
在 wrapper.component.ts 中查看提供程序,在那里,我们告诉 Native MatFormFieldControl,类似于(嗨,我有自己的表单控件,让我们使用它)。
下一步是创建我们的 wrapp
select.component.html
<mat-select [(ngModel)]="value">
<mat-option [value]="item" *ngFor="let item of option; index as j">{{
option[j]
}}</mat-option>
</mat-select>
所以,现在我们可以在 mat-form-field 中的其他组件中使用它
app.component.html
<mat-form-field>
<mat-label>label</mat-label>
<app-select [option]="opt" [(ngModel)]="title"></app-select>
</mat-form-field>
有一些有用的链接:
而现在,我遇到了另一个麻烦 XD
一些我需要如何使用 ngModel,完全在 中,但它一次在 app-select & mat-select 中。当我找到答案时,我会编辑这个
我有一个自定义组件,它是 mat-select 的包装。 我如何在其他组件中使用它?我正在阅读有关 formControl 和 controlValueAccessor 的内容,但我不太了解。
我的custom.html
<mat-select>
<mat-option [value]="opt" *ngFor="let op of opt; index as i">{{opt[i]}}</mat-option>
</mat-select>
<input matInput>
我的custom.ts
import { Component, OnInit, Input } from '@angular/core';
import { MatFormFieldControl } from '@angular/material/form-field';
@Component({
selector: 'cust-input',
templateUrl: './input.component.html',
styleUrls: ['./input.component.scss'],
providers: [{provide: MatFormFieldControl, useExisting: InputComponent}]
})
export class InputComponent extends MatFormFieldControl<string> implements OnInit {
@Input() opt: string;
setDescribedByIds(ids: string[]): void {
throw new Error('Method not implemented.');
}
onContainerClick(event: MouseEvent): void {
throw new Error('Method not implemented.');
}
constructor() {
super();
}
ngOnInit(): void {
}
}
我的app.html
<mat-form-field>
<cust-input [opt]="myopt"></cust-input>
</mat-form-field>
我的app.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
title = 'form';
myopt = ['a', 'b', 'c', 'd'];
}
我不需要像 我的 custmo.html
<mat-form-field>
<mat-select>
<mat-option [value]="opt" *ngFor="let op of opt; index as i">{{opt[i]}}</mat-option>
</mat-select>
<input matInput>
</mat-form-field>
所以答案是: 如果我们想使用我们的自定义 wrappe,我们需要为它创建一个自定义的表单字段控件, 在我们的例子中,我们需要创建自定义 mat-form-field-control
//我们自定义的表单域控件
import { ValueAccessor } from './value-accessor';
import { MatFormFieldControl } from '@angular/material/form-field';
import {
Input,
HostBinding,
Optional,
Self,
ElementRef,
OnDestroy,
Directive,
} from '@angular/core';
import { FocusMonitor } from '@angular/cdk/a11y';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { NgControl } from '@angular/forms';
import { Subject } from 'rxjs';
@Directive()
export class FormControlDirective<T> extends ValueAccessor<T>
implements MatFormFieldControl<T>, OnDestroy {
@Input()
get value() {
return this._value;
}
set value(val: T) {
if (val !== this._value) {
this._value = val;
this.stateChanges.next();
}
}
@Input()
get placeholder() {
return this._placeholder;
}
set placeholder(plc: string) {
this._placeholder = plc;
this.stateChanges.next();
}
@Input()
get required() {
return this._required;
}
set required(req: boolean) {
this._required = coerceBooleanProperty(req);
this.stateChanges.next();
}
get empty() {
return !this._value;
}
constructor(
@Optional() @Self() public ngControl: NgControl,
private fM: FocusMonitor,
private elRef: ElementRef<HTMLElement>
) {
super();
if (this.ngControl != null) {
this.ngControl.valueAccessor = this;
}
fM.monitor(elRef.nativeElement, true).subscribe((origin) => {
this.focused = !!origin;
this.stateChanges.next();
});
}
@HostBinding('class.floating')
get shouldLabelFloat() {
return this.focused || !this.empty;
}
private _value: T | null;
private _placeholder: string;
private _required = false;
nextId = 0;
stateChanges: Subject<void> = new Subject<void>();
focused = false;
@HostBinding() id = `${this.nextId++}`;
errorState = false;
controlType = 'my-select';
autofilled?: boolean;
@HostBinding('attr.aria-desribedby') describedBy = '';
setDescribedByIds(ids: string[]): void {
this.describedBy = ids.join('');
}
onContainerClick(event: MouseEvent): void {
if ((event.target as Element).tagName.toLowerCase() === 'input') {
this.elRef.nativeElement.focus();
}
}
ngOnDestroy(): void {
this.fM.stopMonitoring(this.elRef.nativeElement);
this.stateChanges.complete();
}
}
但是,如果我们想在我们的包装器中使用 ngModel,我们还需要创建我们的自定义控件值访问器
客户价值配件
import { ControlValueAccessor } from '@angular/forms';
export class ValueAccessor<T> implements ControlValueAccessor {
value: T | null;
onChange: Function;
onTouched: Function;
disabled: boolean;
writeValue(val: T): void {
this.value = val;
}
registerOnChange(fn: Function): void {
this.onChange = fn;
}
registerOnTouched(fn: Function): void {
this.onTouched = fn;
}
setDisabledState?(isDisabled: boolean): void {
this.disabled = isDisabled;
}
}
在我们的 custom.wrapper.component.ts 中,我们需要扩展我们的表单控件, 另外,查看custom-form-field-control,并查看constructor,有一个ngControl,我们将其添加到那里,以便一次使用form-control和value-accessor。
select.component.ts
import { Component, Input, OnInit } from '@angular/core';
import { MatFormFieldControl } from '@angular/material/form-field';
import { FormControlDirective } from 'src/app/forms/form-control';
@Component({
selector: 'app-select',
templateUrl: './select.component.html',
styleUrls: ['./select.component.scss'],
providers: [{ provide: MatFormFieldControl, useExisting: SelectComponent }],
})
export class SelectComponent extends FormControlDirective<string>
implements OnInit {
@Input() option: string;
ngOnInit() {}
}
在 wrapper.component.ts 中查看提供程序,在那里,我们告诉 Native MatFormFieldControl,类似于(嗨,我有自己的表单控件,让我们使用它)。 下一步是创建我们的 wrapp
select.component.html
<mat-select [(ngModel)]="value">
<mat-option [value]="item" *ngFor="let item of option; index as j">{{
option[j]
}}</mat-option>
</mat-select>
所以,现在我们可以在 mat-form-field 中的其他组件中使用它
app.component.html
<mat-form-field>
<mat-label>label</mat-label>
<app-select [option]="opt" [(ngModel)]="title"></app-select>
</mat-form-field>
有一些有用的链接:
而现在,我遇到了另一个麻烦 XD 一些我需要如何使用 ngModel,完全在 中,但它一次在 app-select & mat-select 中。当我找到答案时,我会编辑这个