Nested arrays in Angular 2 reactive forms?

后端 未结 1 1389
别那么骄傲
别那么骄傲 2020-11-28 12:22

I have use the following tutorial to create reactive forms in Angular 2 and it works well.

https://scotch.io/tutorials/how-to-build-nested-model-driven-forms-in-angu

相关标签:
1条回答
  • 2020-11-28 12:34

    Using the tutorial above, I have created an 'Organisation' form, which can contain an array of 'Contact' groups. But I am unable to successfully adapt the setup to allow each 'Contact' group to contain an array of 'Email' groups.

    The tutorial above gives you all what you need.

    I suppose you want structure like this.

    Firstly you need some component (AppComponent in my case) where you declare root FormGroup. I called it trustForm below.

    app.component.ts

    export class AppComponent {
      trustForm: FormGroup;
    
      constructor(private fb: FormBuilder) { }
    
      ngOnInit() {
        this.trustForm = this.fb.group({
          name: '',
          contracts: this.fb.array([])
        });
    
        this.addContract();
      }
    
      initContract() {
        return this.fb.group({
          name: '',
          emails: this.fb.array([])
        });
      }
    
      addContract() {
        const contractArray = <FormArray>this.trustForm.controls['contracts'];
        const newContract = this.initContract();
    
        contractArray.push(newContract);
      }
    
      removeContract(idx: number) {
        const contractsArray = <FormArray>this.trustForm.controls['contracts'];
        contractsArray.removeAt(idx);
      }
    }
    

    In this component you have also some methods that help you to manipulate the first level FormArray - contracts

    app.component.html

    <div class="container">
        <form [formGroup]="trustForm">
            <h3>Add trust</h3>
    
            <div class="form-group">
                <label>Name</label>
                <input type="text" class="form-control" formControlName="name">
            </div>
    
            <!--contracts-->
            <div formArrayName="contracts">
                <div *ngFor="let contract of trustForm.controls.contracts.controls; let i=index" class="panel panel-default">
                    <div class="panel-heading">
                        <span>Contract {{i + 1}}</span>
                        <span class="glyphicon glyphicon-remove pull-right" *ngIf="trustForm.controls.contracts.controls.length > 1" (click)="removeContract(i)"></span>
                    </div>
                    <div class="panel-body" [formGroupName]="i">
                        <contract [group]="trustForm.controls.contracts.controls[i]"></contract>
                    </div>
                </div>
            </div>
            <div class="margin-20">
                <button (click)="addContract()" class="btn btn-primary">
                    Add another contract +
                </button>
            </div>
    
        </form>
    
        <h5>Details</h5>
        <pre>{{ trustForm.value | json }}</pre>
    </div>
    

    There is no different from root html from the tutorial except different FormArray name.

    Then you need to build contract component that will be similar to AppComponent

    contract.component.ts

    export class ContractComponent {
        @Input('group') contractGroup: FormGroup;
    
        constructor(private fb: FormBuilder) { }
    
        addEmail() {
            const emailArray = <FormArray>this.contractGroup.controls['emails'];
            const newEmail = this.initEmail();
    
            emailArray.push(newEmail);
        }
    
        removeEmail(idx: number) {
            const emailArray = <FormArray>this.contractGroup.controls['emails'];
            emailArray.removeAt(idx);
        }
    
        initEmail() {
            return this.fb.group({
                text: ''
            });
        }
    }
    

    contract.component.html

    <div [formGroup]="contractGroup">
        <div class="form-group">
            <label>Name</label>
            <input type="text" class="form-control" formControlName="name">
        </div>
    
        <!--emails-->
        <div formArrayName="emails">
            <div *ngFor="let email of contractGroup.controls.emails.controls; let i=index" class="panel panel-default">
                <div class="panel-heading">
                    <span>Email {{i + 1}}</span>
                    <span class="glyphicon glyphicon-remove pull-right" *ngIf="contractGroup.controls.emails.controls.length > 1" (click)="removeEmail(i)"></span>
                </div>
                <div class="panel-body" [formGroupName]="i">
                    <email [group]="contractGroup.controls.emails.controls[i]"></email>
                </div>
            </div>
        </div>
        <div class="margin-20">
            <button (click)="addEmail()" class="btn btn-primary">
                Add another email +
            </button>
        </div>
    
    </div>
    

    As you can see we just replace contracts to emails FormArray and we are also passing FormGroup to email component

    And finally you will only need to fill EmailComponent with desired fields.

    email.component.ts

    export class EmailComponent {
        @Input('group') emailGroup: FormGroup;
    }
    

    email.component.html

    <div [formGroup]="emailGroup">
        <div class="form-group">
            <label>Text</label>
            <input type="text" class="form-control" formControlName="text">
        </div>
    </div>
    

    Completed version you can find at Plunker Example

    If you think that this solution doesn't seems right because the parent component holds the description of the child component like initContract and initEmails you can take a look at more complex

    Plunker Example

    where each component is responsible for its functionality.

    If you're looking for solution for template driven forms read this article:

    • Angular: Nested template driven form
    0 讨论(0)
提交回复
热议问题