Angular 从角度项目中的嵌套组件检索表单数据的最佳方法
我正在处理一个有以下情况的角度项目:有一个组件(ClientViewComponent)包含:Angular 从角度项目中的嵌套组件检索表单数据的最佳方法,angular,Angular,我正在处理一个有以下情况的角度项目:有一个组件(ClientViewComponent)包含: 以标签(ClientOverviewComponent)的形式总结整个客户端表单 用户可以通过不同的“子表单”进行切换以进行编辑(为了简单起见,让我们只关注一个,称为ClientGeneralComponent) 一次仅显示其中一个子表单进行编辑,但概览实时显示子表单中所有输入的值 这就是现在存在的情况(简化为仅显示感兴趣的部分): ClientViewComponent HTML 客户概述 请
- 以标签(ClientOverviewComponent)的形式总结整个客户端表单李>
- 用户可以通过不同的“子表单”进行切换以进行编辑(为了简单起见,让我们只关注一个,称为ClientGeneralComponent)李>
客户概述
请选择:
客户端视图组件TS
ngOnInit(){
this.clientForm=this.fb.group({
通用组:this.fb.group({
名称:['',[Validators.required,Validators.maxLength(256)],
代码:['',[Validators.required,Validators.maxLength(32)],
addressGroup:this.fb.group({
地址:['',[Validators.required,Validators.maxLength(512)],
地址2:[''[Validators.maxLength(512)],
城市:['',[Validators.required,Validators.maxLength(256)],
状态:['',[Validators.required,Validators.maxLength(64)],
zipCode:[“”,[Validators.required,Validators.maxLength(64)],
国家:[''[Validators.required]],
})
})
});
}
客户端概览HTML
*客户端名称:{{generalGroup.controls['Name'].value}
*客户端代码:{{generalGroup.controls['Code'].value}
*地址:{{addressGroup.controls['Address'].value}
地址#2:{{addressGroup.controls['address2'].value}
*城市:{{addressGroup.controls['City'].value}
*状态:{{addressGroup.controls['State'].value}
*国家:{{Country}
*邮政编码:{{addressGroup.controls['zipCode'].value}
客户端概览组件TS
导出类ClientOverview组件实现OnInit{
@输入('数据')
设置数据(值:ClientOverviewData){
此.setData(值);
}
一般组:FormGroup;
地址组:FormGroup;
@输入('generalGroup')
设置GeneralGroup(值:FormGroup){
this.generalGroup=值;
this.addressGroup=value.get('addressGroup')作为FormGroup;
此参数为.refreshClientValues();
}
ClientGeneralComponent HTML只是一个带有formGroup和相应formControlName输入的常规表单
客户端通用组件TS
导出类ClientGeneralComponent实现OnInit{
@输入('数据')
设置数据(值:ClientGeneralData){
此.setData(值);
}
formGroup:formGroup;
地址组:FormGroup;
@输入('formGroup')
设置FormGroup(值:FormGroup){
this.formGroup=值;
this.addressGroup=value.get('addressGroup')作为FormGroup;
}
现在,所有这些都很好。完整的表单按预期工作,用户填写通用组件的输入,值实时显示在概述中。当我开始对组件进行单元测试时,问题出现了。问题是,对于ClientGeneralComponent的测试类,我必须声明表单gro我的想法是,如果我可以将常规表单初始化从ClientViewComponent移动到ClientGeneralComponent(实际上包含控件),那么就不需要在测试类中复制表单初始化代码,如下所示:
beforeach(()=>{
fixture=TestBed.createComponent(ClientGeneralComponent);
组件=fixture.componentInstance;
component.formGroup=fb.group({
名称:['',[Validators.required,Validators.maxLength(256)],
代码:['',[Validators.required,Validators.maxLength(32)],
地址组:fb.group({
地址:['',[Validators.required,Validators.maxLength(512)],
地址2:[''[Validators.maxLength(512)],
城市:['',[Validators.required,Validators.maxLength(256)],
状态:['',[Validators.required,Validators.maxLength(64)],
zipCode:[“”,[Validators.required,Validators.maxLength(64)],
国家:[''[Validators.required]]
})
});
fixture.detectChanges();
});
但是如果我将常规表单init代码移动到子表单,我会在Overview组件中得到“cannotreadproperty of undefined”错误,因为它没有初始化“generalGroup”所以我的问题是,是否有办法做到这一点,在表单组所属的子组件中初始化表单组,并且仍然能够将其绑定到父表单中
感谢您的帮助!这是上的工作代码 它的实现与我在评论中解释的有点不同 ClientForm位于ClientGeneralComponent内部。在form.valueChange上,它发出新的客户端对象。这由父ClientViewComponent拾取并发送到另一个ClientOverviewComponent。角度变化检测负责更新所有组件中的值 客户端通用组件.ts
import { Component, OnInit, OnDestroy, Input, Output, EventEmitter } from '@angular/core';
import { FormGroup, Validators, FormBuilder } from '@angular/forms';
import { Observable, of, Subject } from 'rxjs';
import { startWith, map, takeUntil } from 'rxjs/operators';
import { IClient } from './client';
@Component({
selector: 'client-general',
templateUrl: './client-general.component.html'
})
export class ClientGeneralComponent implements OnInit, OnDestroy {
clientForm: FormGroup;
@Output()
clientObj: EventEmitter<IClient>;
destroy$: Subject<boolean>;
constructor(private fb: FormBuilder) {
this.destroy$ = new Subject<boolean>();
this.clientObj = new EventEmitter<IClient>();
}
ngOnInit() {
this.clientForm = this.fb.group({
generalGroup: this.fb.group({
name: ['', [Validators.required, Validators.maxLength(256)]]
})
});
this.clientForm.valueChanges
.pipe(takeUntil(this.destroy$))
.subscribe(() => this.clientObj.emit(this.clientForm.getRawValue()));
}
ngOnDestroy(): void {
this.destroy$.next(true);
this.destroy$.unsubscribe();
}
}
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { FormGroup, Validators, FormBuilder } from '@angular/forms';
import { IClient } from './client';
@Component({
selector: 'client-view',
templateUrl: './client-view.component.html'
})
export class ClientViewComponent implements OnInit {
clientObj: IClient;
constructor(private fb: FormBuilder) {
}
ngOnInit() {
}
}
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { FormGroup, Validators, FormBuilder } from '@angular/forms';
import { IClient } from './client';
@Component({
selector: 'client-overview',
templateUrl: './client-overview.component.html'
})
export class ClientOverviewComponent implements OnInit {
@Input()
clientObj: IClient;
ngOnInit() {
}
}
客户端视图.component.html
<div [formGroup]="clientForm.controls['generalGroup']">
<h3>General</h3>
<div>
<span>Client Name</span>
</div>
<div>
<input type="text" id="name" name="name" formControlName="name"/>
</div>
</div>
<div>
<h2>Client Overview</h2>
<client-overview
[clientObj]="clientObj"></client-overview>
</div>
<div>
<h2>Please choose: </h2>
<client-general (clientObj)="clientObj=$event"></client-general>
</div>
<div>
<div>
<h3>General</h3>
<div>
<span>Name: {{clientObj?.generalGroup.name}}</span>
</div>
</div>
</div>
客户端概述.component.html
<div [formGroup]="clientForm.controls['generalGroup']">
<h3>General</h3>
<div>
<span>Client Name</span>
</div>
<div>
<input type="text" id="name" name="name" formControlName="name"/>
</div>
</div>
<div>
<h2>Client Overview</h2>
<client-overview
[clientObj]="clientObj"></client-overview>
</div>
<div>
<h2>Please choose: </h2>
<client-general (clientObj)="clientObj=$event"></client-general>
</div>
<div>
<div>
<h3>General</h3>
<div>
<span>Name: {{clientObj?.generalGroup.name}}</span>
</div>
</div>
</div>
一般的