如何使用当前的 Form API 将父组件的 FormGroup 传递给其子组件
Posted
技术标签:
【中文标题】如何使用当前的 Form API 将父组件的 FormGroup 传递给其子组件【英文标题】:How can I pass the FormGroup of a parent component to its child component using the current Form API 【发布时间】:2016-11-27 14:14:52 【问题描述】:我想将父组件的 FormGroup
传递给其子组件,以便使用子组件显示错误消息。
给定以下父级:
parent.component.ts
import Component, OnInit from '@angular/core'
import
REACTIVE_FORM_DIRECTIVES, AbstractControl, FormBuilder, FormControl, FormGroup, Validators
from '@angular/forms'
@Component(
moduleId: module.id,
selector: 'parent-cmp',
templateUrl: 'language.component.html',
styleUrls: ['language.component.css'],
directives: [ErrorMessagesComponent]
)
export class ParentCmp implements OnInit
form: FormGroup;
first: AbstractControl;
second: AbstractControl;
constructor(private _fb: FormBuilder)
this.first = new FormControl('');
this.second = new FormControl('')
ngOnInit()
this.form = this._fb.group(
'first': this.first,
'second': this.second
);
我现在想将上面的 form:FormGroup 变量传递给下面的子组件:
error-message.component.ts
import Component, OnInit, Input from '@angular/core'
import NgIf from '@angular/common'
import REACTIVE_FORM_DIRECTIVES, FormGroup from '@angular/forms'
@Component(
moduleId: module.id,
selector: 'epimss-error-messages',
template: `<span class="error" *ngIf="errorMessage !== null">errorMessage</span>`,
styles: [],
directives: [REACTIVE_FORM_DIRECTIVES, NgIf]
)
export class ErrorMessagesComponent implements OnInit
@Input() ctrlName: string
constructor(private _form: FormGroup)
ngOnInit()
get errorMessage()
// Find the control in the Host (Parent) form
let ctrl = this._form.find(this.ctrlName);
console.log('ctrl| ', ctrl);
// for (let propertyName of ctrl.errors)
// // If control has a error
// if (ctrl.errors.hasOwnProperty(propertyName) && ctrl.touched)
// // Return the appropriate error message from the Validation Service
// return CustomValidators.getValidatorErrorMessage(propertyName);
//
//
return null;
构造函数 formGroup 代表父级的 FormGroup - 在当前形式下它不起作用。
我正在尝试在http://iterity.io/2016/05/01/angular/angular-2-forms-and-advanced-custom-validation/ 上遵循这个过时的示例
【问题讨论】:
Here's another idea 您是否使用提供的任何答案解决了这个问题? 【参考方案1】:我会将表单作为输入传递给子组件;
@Component(
moduleId: module.id,
selector: 'epimss-error-messages',
template: `
<span class="error" *ngIf="errorMessage !== null">errorMessage</span>`,
styles: [],
directives: [REACTIVE_FORM_DIRECTIVES, NgIf]
)
export class ErrorMessagesComponent implements OnInit
@Input()
ctrlName: string
@Input('form') _form;
ngOnInit()
this.errorMessage();
errorMessage()
// Find the control in the Host (Parent) form
let ctrl = this._form.find(this.ctrlName);
console.log('ctrl| ', ctrl)
// for (let propertyName of ctrl.errors)
// // If control has a error
// if (ctrl.errors.hasOwnProperty(propertyName) && ctrl.touched)
// // Return the appropriate error message from the Validation Service
// return CustomValidators.getValidatorErrorMessage(propertyName);
//
//
return null;
当然,您需要将表单从父组件传递给子组件,您可以通过不同的方式来实现,但最简单的是:
在你父母的某个地方;
<epimss-error-messages [form]='form'></epimss-error-messages>
【讨论】:
我认为private _form: FormGroup)
应该从构造函数参数列表中删除。【参考方案2】:
在父组件中这样做:
<div [formGroup]="form">
<div>Your parent controls here</div>
<your-child-component [formGroup]="form"></your-child-component>
</div>
然后在您的子组件中,您可以像这样获取该引用:
export class YourChildComponent implements OnInit
public form: FormGroup;
// Let Angular inject the control container
constructor(private controlContainer: ControlContainer)
ngOnInit()
// Set our form property to the parent control
// (i.e. FormGroup) that was passed to us, so that our
// view can data bind to it
this.form = <FormGroup>this.controlContainer.control;
您甚至可以通过更改组件的选择器来确保在组件上指定 formGroupName
或 [formGroup]
,如下所示:
selector: '[formGroup] epimss-error-messages,[formGroupName] epimss-error-messages'
这个答案应该足以满足您的需求,但如果您想了解更多,我在这里写了一篇博文:
https://mrpmorris.blogspot.co.uk/2017/08/angular-composite-controls-formgroup-formgroupname-reactiveforms.html
【讨论】:
在 Angular 8this.controlContainer.control as FormGroup
中返回以下错误 'AbstractControl' 类型的参数不可分配给 'FormControl' 类型的参数'
在 Angular 8 中出现错误:Error: "No value accessor for form control with name: 'right'"
我能够让它在 8 和 9 中工作。关键是要确保我标记了父 [formGroup]。 viewProviders: [ provide: ControlContainer, useExisting: FormGroupDirective ]
。如果这不起作用,请尝试搜索“useExisting: FormGroupDirective”。【参考方案3】:
这是在父 formGroup 中使用的子组件的示例: 子组件 ts:
import Component, OnInit, Input from '@angular/core';
import FormGroup, ControlContainer, FormControl from '@angular/forms';
@Component(
selector: 'app-date-picker',
template: `
<mat-form-field [formGroup]="form" style="width:100%;">
<input matInput [matDatepicker]="picker" [placeholder]="placeHolder" [formControl]="control" readonly>
<mat-datepicker-toggle matSuffix [for]="picker"></mat-datepicker-toggle>
<mat-datepicker #picker></mat-datepicker>
</mat-form-field>
<mat-icon (click)="clearDate()">replay</mat-icon>`,
styleUrls: ['./date-picker.component.scss']
)
export class DatePickerComponent implements OnInit
public form: FormGroup;
public control : FormControl;
@Input() controlName : string;
@Input() placeHolder : string;
constructor(private controlContainer: ControlContainer)
clearDate()
this.control.reset();
ngOnInit()
this.form = <FormGroup>this.controlContainer.control;
this.control = <FormControl>this.form.get(this.controlName);
css 日期选择器:
mat-icon
position: absolute;
left: 83%;
top: 31%;
transform: scale(0.9);
cursor: pointer;
并像这样使用:
<app-date-picker class="col-md-4" [formGroup]="feuilleForm" controlName="dateCreation" placeHolder="Date de création"></app-date-picker>
【讨论】:
【参考方案4】:如果要从子组件访问父组件,可以访问FormControl实例的父属性https://angular.io/api/forms/AbstractControl#parent
获取父级错误:
const parent = control.parent;
const errors = parent.errors;
【讨论】:
【参考方案5】:父组件:
@Component(
selector: 'app-arent',
templateUrl: `<form [formGroup]="parentFormGroup" #formDir="ngForm">
<app-child [formGroup]="parentFormGroup"></app-child>
</form> `
)
export class ParentComponent implements
parentFormGroup :formGroup
ngOnChanges()
console.log(this.parentFormGroup.value['name'])
子组件:
@Component(
selector: 'app-Child',
templateUrl: `<form [formGroup]="childFormGroup" #formDir="ngForm">
<input id="nameTxt" formControlName="name">
</form> `
)
export class ChildComponent implements OnInit
@Input() formGroup: FormGroup
childFormGroup :FormGroup
ngOnInit()
// Build your child from
this.childFormGroup.addControl('name', new FormControl(''))
/* Bind your child form control to parent form group
changes in 'nameTxt' directly reflect to your parent
component formGroup
*/
this.formGroup.addControl("name", this.childFormGroup.controls.name);
【讨论】:
请问两个组件中的#formDir="ngForm" 是否与将父窗体传递给子窗体的工作有关? 不,两个组件都有自己的#formDir="ngForm",它们之间没有关系。【参考方案6】:ngOnInit
很重要 - 这在构造函数中不起作用。
而且我更喜欢寻找FormControlDirective
- 它是在子组件的祖先层次结构中找到的第一个
constructor(private formGroupDirective: FormGroupDirective)
ngOnInit()
this.formGroupDirective.control.addControl('password', this.newPasswordControl);
this.formGroupDirective.control.addControl('confirmPassword', this.confirmPasswordControl);
this.formGroup = this.formGroupDirective.control;
【讨论】:
【参考方案7】:我会这样做,我已将子表单数据作为组传递给父表单,因此您可以在提交调用中分离表单数据。
家长:
<form [formGroup]="registerStudentForm" (ngSubmit)="onSubmit()">
<app-basic-info [breakpoint]="breakpoint" [formGroup]="registerStudentForm"></app-basic-info>
<button mat-button>Submit</button>
</form>
孩子:
<mat-card [formGroup]="basicInfo">
<mat-card-title>Basic Information</mat-card-title>
<mat-card-content>
<mat-grid-list
[gutterSize]="'20px'"
[cols]="breakpoint"
rowHeight="60px"
>
<mat-grid-tile>
<mat-form-field appearance="legacy" class="full-width-field">
<mat-label>Full name</mat-label>
<input matInput formControlName="full_name" />
</mat-form-field>
</mat-grid-tile>
</mat-grid-list>
</mat-card-content>
</mat-card>
Parent.ts:
export class RegisterComponent implements OnInit
constructor()
registerForm = new FormGroup();
onSubmit()
console.warn(this.registerForm.value);
Child.ts
export class BasicInfoComponent implements OnInit
@Input() breakpoint;
@Input() formGroup: FormGroup;
basicInfo: FormGroup;
constructor()
ngOnInit(): void
this.basicInfo = new FormGroup(
full_name: new FormControl('Riki maru'),
dob: new FormControl(''),
);
this.formGroup.addControl('basicInfo', this.basicInfo);
在您的子表单组件中@Input() formGroup: FormGroup;
部分将是父组件的引用
【讨论】:
【参考方案8】:对于 Angular 11,我尝试了上述所有答案,并采用了不同的组合,但没有什么对我有用。所以我最终得到了以下解决方案,它对我来说就像我想要的那样。
打字稿
@Component(
selector: 'fancy-input',
templateUrl: './fancy-input.component.html',
styleUrls: ['./fancy-input.component.scss']
)
export class FancyInputComponent implements OnInit
valueFormGroup?: FormGroup;
valueFormControl?: FormControl;
constructor(
private formGroupDirective: FormGroupDirective,
private formControlNameDirective: FormControlName
)
ngOnInit()
this.valueFormGroup = this.formGroupDirective.form;
this.valueFormControl = this.formGroupDirective.getControl(this.formControlNameDirective);
get controlName()
return this.formControlNameDirective.name;
get enabled()
return this.valueFormControl?.enabled
HTML
<div *ngIf="valueFormGroup && valueFormControl">
<!-- Edit -->
<div *ngIf="enabled; else notEnabled" [formGroup]="valueFormGroup">
<input class="input" type="text" [formControlName]="controlName">
</div>
<!-- View only -->
<ng-template #notEnabled>
<div>
valueFormControl?.value
</div>
</ng-template>
</div>
用法
请注意,我必须添加 ngDefaultControl
否则它不会在控制台中给出默认值访问器错误(如果有人知道如何在没有错误的情况下摆脱它 - 将不胜感激)。
<form [formGroup]="yourFormGroup" (ngSubmit)="save()">
<fancy-input formControlName="yourFormControlName" ngDefaultControl></fancy-input>
</form>
【讨论】:
似乎回答如何摆脱No value accessor for form control...
错误在这里***.com/questions/45659742/… 和相关NG_VALUE_ACCESSOR
拥有构造函数参数private formGroupDirective: FormGroupDirective
拯救了我的一天。以上是关于如何使用当前的 Form API 将父组件的 FormGroup 传递给其子组件的主要内容,如果未能解决你的问题,请参考以下文章
使用 redux-form 和 Fetch API 进行服务器验证
关于WinForm 中 调用SetParent这个API的问题