Angular 从角度项目中的嵌套组件检索表单数据的最佳方法

Angular 从角度项目中的嵌套组件检索表单数据的最佳方法,angular,Angular,我正在处理一个有以下情况的角度项目:有一个组件(ClientViewComponent)包含: 以标签(ClientOverviewComponent)的形式总结整个客户端表单 用户可以通过不同的“子表单”进行切换以进行编辑(为了简单起见,让我们只关注一个,称为ClientGeneralComponent) 一次仅显示其中一个子表单进行编辑,但概览实时显示子表单中所有输入的值 这就是现在存在的情况(简化为仅显示感兴趣的部分): ClientViewComponent HTML 客户概述 请

我正在处理一个有以下情况的角度项目:有一个组件(ClientViewComponent)包含:

  • 以标签(ClientOverviewComponent)的形式总结整个客户端表单
  • 用户可以通过不同的“子表单”进行切换以进行编辑(为了简单起见,让我们只关注一个,称为ClientGeneralComponent)
一次仅显示其中一个子表单进行编辑,但概览实时显示子表单中所有输入的值

这就是现在存在的情况(简化为仅显示感兴趣的部分):

ClientViewComponent HTML


客户概述
请选择:
客户端视图组件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>

一般的