Как я могу заставить мой вложенный массив формы умножаться при исправлении значения родительской формы? - PullRequest
0 голосов
/ 26 марта 2020

У меня есть огромная форма, которую я разбил на мелкие компоненты размером с укус. Я делаю это, передавая свою форму компоненту и имея этот компонент t ie в своих собственных полях для формы, например, так:

Основная форма

<h3>Not previously treated with</h3>
<app-treatment-group [form]="form.get('patients')" [formName]="'notPreviouslyTreated'"></app-treatment-group>

1-й слой вложенности (группа обработки)

<ng-container [formGroup]="form">
    <div class="questionGroup multipliable vertical hasMultiplicationText" [formArrayName]="formName"
         *ngFor="let outerTreatment of form.get(formName)?.controls; let i = index; let lastOuter = last">

        <app-multiplication-buttons
            [elementCount]="form.get(formName)?.controls.length"
            (add)="addTreatment()"
            (remove)="removeTreatment(i)"
            [tooltipText]="'Add another group'"></app-multiplication-buttons>
        <app-treatment [form]="outerTreatment"></app-treatment>

        <h4 *ngIf="!lastOuter" class="multiplicationText">Or</h4>
    </div>
</ng-container>
@Component({
    selector: 'app-treatment-group',
    templateUrl: './treatment-group.component.html',
    styleUrls: ['./treatment-group.component.css']
})
export class TreatmentGroupComponent implements OnInit {
    @Input() public form: FormGroup;
    @Input() public formName: string;

    private subscriptions: { [key: string]: Subscription } = {};

    constructor(
        private formBuilder: FormBuilder
    ) {
    }

    ngOnInit() {
        // We need to add controls asynchronously to avoid ExpressionChangedAfterItHasBeenCheckedError errors
        // https://stackoverflow.com/questions/45661010/dynamic-nested-reactive-form-expressionchangedafterithasbeencheckederror
        Promise.resolve(null).then(() => {
            this.createForm();
            this.subscriptions.formValueChanges = this.form.get(this.formName).valueChanges.subscribe((value) => {
                console.log(value);
            });
        });
    }

    private createForm() {
        this.form.addControl(this.formName, this.formBuilder.array([this.creatTreatment()
        ]));
    }

    private creatTreatment () {
        return this.formBuilder.group({});
    }

    private addTreatment() {
        (this.form.get(this.formName) as FormArray).push(this.creatTreatment());
    }

    public removeTreatment(i: number): void {
        (this.form.get(this.formName) as FormArray).removeAt(i);
    }
}

2-й слой вложенности (обработка)

<ng-container [formGroup]="form" *ngIf="shouldDisplay">
    <div class="questionGroup multipliable"
         formArrayName="treatmentGroup"
         *ngFor="let innerTreatment of form.get('treatmentGroup')['controls']; let i = index">

        <ng-container [formArrayName]="i">
            <div class="question">
                <app-mapping-input
                    formControlName="treatment"
                    [label]="labels.name"
                    [requiredIndicator]="shouldBeRequired"
                    [usage]="'drug'"></app-mapping-input> <!-- something with a Control Value Accessor -->
                <app-error [control]="innerTreatment.get('treatment')"></app-error>
            </div>

            <div class="question">
                <label for="treatmentDetails-{{i}}-{{uniqueIdentifier}}">{{labels.details}}</label>
                <input type="text" id="treatmentDetails-{{i}}-{{uniqueIdentifier}}"
                       formControlName="treatmentDetails">
            </div>

            <app-multiplication-buttons
                [elementCount]="form.get('treatmentGroup')['controls'].length"
                (add)="addTreatmentInner()"
                (remove)="removeTreatmentInner(i)"
                [tooltipText]="'Add another treatment'">
            </app-multiplication-buttons>
        </ng-container>
    </div>
</ng-container>
@Component({
    selector: 'app-treatment',
    templateUrl: './treatment.component.html',
    styleUrls: ['./treatment.component.css', './../../curation-common.css']
})
export class TreatmentComponent implements OnInit, OnDestroy, AfterViewInit {

    @Input() form: FormGroup;
    @Input() labels = {
        name: 'Treatment name',
        details: 'Treatment details',
    };
    @Input() shouldBeRequired = false;  // WARNING: Do *NOT* rename this property to required, it will cause aExpressionChangedAfterItHasBeenCheckedError

    public uniqueIdentifier = Math.floor(Math.random() * 1000000);
    public subscriptions = {};
    public shouldDisplay = false;

    constructor(private formBuilder: FormBuilder) {this.setRequiredValidator();}

    ngOnInit() {
        Promise.resolve(null).then(() => {
            this.createForm();
            this.shouldDisplay = true;
        });
    }

    ngOnDestroy() {CommonService.unsubscribeFromAll(this.subscriptions);}

    public addTreatmentInner(): void {
        (this.form.get('treatmentGroup') as FormArray).push(this.createTreatmentInner());
    }

    public removeTreatmentInner(i: number): void {
        (this.form.get('treatmentGroup') as FormArray).removeAt(i);
    }

    private createForm() {
            this.form.addControl('treatmentGroup', this.formBuilder.array([this.createTreatmentInner()]));
    }

    private createTreatmentInner(): FormGroup {
        return this.formBuilder.group({
            treatment: ['', [mappedValidator()]],
            treatmentDetails: ['']
        });
    }

    private setRequiredValidator() {
        if (this.shouldBeRequired) {
            this.form.setValidators([Validators.required]);
            this.form.updateValueAndValidity();
        }
    }
}

Проблема

Теперь проблема в том, что, когда я получаю вложенный объект от серверной части (под нагрузкой), мой массив не будет добавлять или удалять элементы, когда я исправляю форму со значением. Я понимаю, что это нормальное поведение, но, с другой стороны, я не вижу способа добавлять или удалять элементы формы без тесного связывания основной формы с дочерней формой.

То, что я считал

Я подумал о том, чтобы просто указать количество элементов массива форм app-treatment-group, и его дочерний элемент должен быть получен через @Input(), но это обходной путь, а не правильное решение.

Я также подумал, что могу просто подписаться если значение формы изменяется, перехватывает объект и создает или удаляет элементы массива формы по мере необходимости, но при изменении значения я просто получаю усеченный объект.

Хотя однокомпонентные формы довольно просты и будут безусловно, решит мои проблемы, эта опция неосуществима, так как я работаю со многими различными формами, каждая из которых состоит из набора компонентов с одинаковыми полями и обрабатывает эти формы и приведет к большому количеству повторений кода. Поэтому я ищу решение, которое работает, будучи инкапсулированным в его собственный компонент или с минимальной связью между ним и его родителем.

...