@Component({ selector: 'field',template: ` <div class="form-group"> <label class="control-label">{{label}}</label> <ng-content></ng-content> <!-- Will display the field --> <!-- Here,put error display logic --> </div> ` }) export class FormFieldComponent { @Input() label: string; // Field label @Input() theControl: FormControl; // Current form control,required to display errors }
在FormFieldComponent中,我需要一个FormControl实例来显示错误.
我的表单看起来像这样:
<form [formGroup]="myForm"> ... <field label="Title" [theControl]="myForm.get('title')"> <input type="text" formControlName="title"> </field> ... </form>
但我对上面的代码并不完全满意.如您所见,我在两个位置指定字段的键:在[theControl]输入属性和formControlName指令中.
如果我能写的话,代码会更简洁:
<field label="Title"> <input type="text" formControlName="title"> </field>
注意[theControl]输入属性是如何消失的. FieldComponent应该能够获得它包含的FormControl实例,但是如何实现呢?
我已经尝试使用@ContentChildren装饰器来查询FormControl指令的组件模板,但它不起作用:
export class FormFieldComponent { @ContentChildren(FormControlDirective) theControl: any; }
另一种选择是将字段的键作为输入传递给FormFieldComponent,然后让组件使用该键:
>以编程方式将formControlName指令应用于它包含的字段.
>获取其父级< form>,访问相应的FormGroup实例,并从中提取FormControl实例.
你不能. (好吧,也许你可以,但它会是hacky!)
答案很长:你不能,但……
FormControl不可注射.指令是可注入的,但是,您必须处理formControlName,ngModel,formControl等,并且它们不能从包装组件访问,但它的子代…
对于你的情况,你可以试试@ContentChildren(FormControlName)theControl:any;因为你的代码中没有暗示FormControlDirective,但你无论如何都无法访问FormControl(属性_control是内部的,所以它会是一个hack)…
因此,您应该坚持从处理FormGroup的组件中管理错误.
但是,如果你想显示一个自定义输入(不会显示错误信息,但能够显示此输入处于错误状态(主机元素将获得ng-valid,ng-invalid类,所以它只是你可以通过实施ControlValueAccessor
来实现这一点.
A bridge between a control and a native element.
A ControlValueAccessor abstracts the operations of writing a new value to a DOM element representing an input control.
这意味着实现此接口的指令/组件可以与ngModel,formControl等一起使用…
例如:< my-component [(ngModel)] =“foo”>< / my-component>
它不是你问题的确切复制品,但是这个实现为我解决了同样的问题:
export const INPUT_VALUE_ACCESSOR: any = { provide: NG_VALUE_ACCESSOR,useExisting: forwardRef(() => InputComponent),multi: true }; @Component({ selector: "field",template: `<!--put anything you want in your template--> <label>{{label}}</label> <input #input (input)="onChange($event.target.value)" (blur)="onTouched()" type="text">`,styles: [],providers: [INPUT_VALUE_ACCESSOR] }) export class InputComponent implements ControlValueAccessor { @ViewChild("input") input: ElementRef; @Input() label:string; onChange = (_: any) => { }; onTouched = () => { }; constructor(private _renderer: Renderer) { } writeValue(value: any): void { const normalizedValue = value == null ? "" : value; this._renderer.setElementProperty(this.input.nativeElement,"value",normalizedValue); } registerOnChange(fn: (_: any) => void): void { this.onChange = fn; } registerOnTouched(fn: () => void): void { this.onTouched = fn; } setDisabledState(isDisabled: boolean): void { this._renderer.setElementProperty(this.input.nativeElement,"disabled",isDisabled); } }
那你就可以:
<field label="Title" formControlName="title"></field>