SAMPLE PLUNKER
我看到两个选项:
- 从部件
FormControl
传播错误<select>
FormControl
每当<select>
FormControl
值改变
- 从部件
FormControl
传播的验证到<select>
FormControl
娄以下变量是可用的:
selectModel
是的<select>
formControl
的NgModel
是接收作为参数的组件的FormControl
选项1:传播错误
ngAfterViewInit(): void {
this.selectModel.control.valueChanges.subscribe(() => {
this.selectModel.control.setErrors(this.formControl.errors);
});
}
选项2:传播校验
ngAfterViewInit(): void {
this.selectModel.control.setValidators(this.formControl.validator);
this.selectModel.control.setAsyncValidators(this.formControl.asyncValidator);
}
两者之间的差异在于传播错误意味着已经有错误,而秒选项涉及第二次执行验证程序。其中一些,如异步验证器可能执行成本太高。
传播所有属性?
传播所有属性没有通用的解决方案。各种性质由各种指令或其他手段设定,因此具有不同的生命周期,这意味着需要特定的处理。当前的解决方案涉及传播验证错误和验证器。那里有很多物业。
请注意,通过订阅FormControl.statusChanges()
,您可能会从FormControl
实例获得不同的状态更改。通过这种方式,您可以获取控件是否为VALID
,INVALID
,DISABLED
或PENDING
(异步验证仍在运行)。
验证如何在引擎盖下工作?
在引擎盖下,使用指令(check the source code)应用验证器。指令有providers: [REQUIRED_VALIDATOR]
这意味着自己的分层注入器用于注册验证器实例。因此,根据元素上应用的属性,指令将在与目标元素关联的注入器中添加验证器实例。
接下来,通过NgModel
和FormControlDirective
检索这些验证器。
校验以及值存取检索等:
constructor(@Optional() @Host() parent: ControlContainer,
@Optional() @Self() @Inject(NG_VALIDATORS) validators: Array<Validator|ValidatorFn>,
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: Array<AsyncValidator|AsyncValidatorFn>,
@Optional() @Self() @Inject(NG_VALUE_ACCESSOR)
和分别为:
constructor(@Optional() @Self() @Inject(NG_VALIDATORS) validators: Array<Validator|ValidatorFn>,
@Optional() @Self() @Inject(NG_ASYNC_VALIDATORS) asyncValidators: Array<AsyncValidator|AsyncValidatorFn>,
@Optional() @Self() @Inject(NG_VALUE_ACCESSOR)
valueAccessors: ControlValueAccessor[])
注意@Self()
被使用,因此自己的喷射器(这是该指令是作为元件的应用)被用来获得依赖关系。
NgModel
和FormControlDirective
有一个实例FormControl
它实际上更新值并执行验证程序。
因此,与之交互的要点是FormControl
实例。
此外,所有验证程序或值存取程序都在它们所应用的元素的注入器中注册。这意味着父母不应该访问该注射器。因此,从当前组件访问由<select>
提供的喷射器将是一种不好的做法。对于选项1
示例代码(由选项2容易更换)
下面的示例具有两个验证:其中一个是必需的,另一个是迫使选项,以匹配“选项3”的图案。
The PLUNKER
options.component.ts
import {AfterViewInit, Component, forwardRef, Input, OnInit, ViewChild} from '@angular/core';
import {ControlValueAccessor, FormControl, NG_VALUE_ACCESSOR, NgModel} from '@angular/forms';
import {SettingsService} from '../settings.service';
const OPTIONS_VALUE_ACCESSOR: any = {
multi: true,
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => OptionsComponent)
};
@Component({
providers: [OPTIONS_VALUE_ACCESSOR],
selector: 'inf-select[name]',
templateUrl: './options.component.html',
styleUrls: ['./options.component.scss']
})
export class OptionsComponent implements ControlValueAccessor, OnInit, AfterViewInit {
@ViewChild('selectModel') selectModel: NgModel;
@Input() formControl: FormControl;
@Input() name: string;
@Input() disabled = false;
private propagateChange: Function;
private onTouched: Function;
private settingsService: SettingsService;
selectedValue: any;
constructor(settingsService: SettingsService) {
this.settingsService = settingsService;
}
ngOnInit(): void {
if (!this.name) {
throw new Error('Option name is required. eg.: <options [name]="myOption"></options>>');
}
}
ngAfterViewInit(): void {
this.selectModel.control.valueChanges.subscribe(() => {
this.selectModel.control.setErrors(this.formControl.errors);
});
}
writeValue(obj: any): void {
this.selectedValue = obj;
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
}
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
}
}
options.component.html
<select #selectModel="ngModel"
class="form-control"
[disabled]="disabled"
[(ngModel)]="selectedValue"
(ngModelChange)="propagateChange($event)">
<option value="">Select an option</option>
<option *ngFor="let option of settingsService.getOption(name)" [value]="option.description">
{{option.description}}
</option>
</select>
options.component。SCSS
:host {
display: inline-block;
border: 5px solid transparent;
&.ng-invalid {
border-color: purple;
}
select {
border: 5px solid transparent;
&.ng-invalid {
border-color: red;
}
}
}
用法
定义FormControl
实例:
export class AppComponent implements OnInit {
public control: FormControl;
constructor() {
this.control = new FormControl('', Validators.compose([Validators.pattern(/^option 3$/), Validators.required]));
}
...
绑定FormControl
实例的组件:
<inf-select name="myName" [formControl]="control"></inf-select>
虚拟设置服务
/**
* TODO remove this class, added just to make injection work
*/
export class SettingsService {
public getOption(name: string): [{ description: string }] {
return [
{ description: 'option 1' },
{ description: 'option 2' },
{ description: 'option 3' },
{ description: 'option 4' },
{ description: 'option 5' },
];
}
}
您可以在重新生成它吗? – yurzui