it-swarm.com.de

Angular 2 verschachtelte Formulare mit untergeordneten Komponenten und Validierung

Ich versuche ein verschachteltes Formular mit Bestätigung in Angular 2 zu erreichen. Ich habe Beiträge gesehen und die Dokumentation befolgt, aber ich habe wirklich Probleme, ich hoffe, Sie können mich in die richtige Richtung weisen.

Was ich versuche zu erreichen, ist ein validiertes Formular mit mehreren untergeordneten Komponenten. Diese Kinderkomponenten sind ein bisschen komplex, einige von ihnen haben mehr Kinderkomponenten, aber meiner Meinung nach können wir das Problem mit einem Elternteil und einem Kind angreifen.

Was versuche ich zu erreichen

Ein Formular haben, das so funktioniert:

<div [formGroup]="userForm" novalidate>
    <div>
        <label>User Id</label>
        <input formControlName="userId">
    </div>
    <div>
        <label>Dummy</label>
        <input formControlName="dummyInput">
    </div>
</div>

Dies erfordert eine Klasse wie diese:

private userForm: FormGroup;
constructor(private fb: FormBuilder){
    this.createForm();
}
private createForm(): void{
    this.userForm = this.fb.group({
        userId: ["", Validators.required],
        dummyInput: "", Validators.required]
    });
}

Das funktioniert wie erwartet, aber jetzt möchte ich den Code entkoppeln und die "dummyInput" -Funktionalität in eine separate, andere Komponente stecken. Hier verirre ich mich. Das habe ich ausprobiert, ich glaube, ich bin nicht weit davon entfernt, die Antwort zu bekommen, aber ich habe wirklich keine Ideen, ich bin ziemlich neu in der Szene:

parent.component.html

<div [formGroup]="userForm" novalidate>
    <div>
        <label>User Id</label>
        <input formControlName="userId">
    </div>
    <div>
        <dummy></dummy>
    </div>
</div>

parent.component.ts

private createForm(): void{
    this.userForm = this.fb.group({
    userId: ["", Validators.required],
    dummy: this.fb.group({
        dummyInput: ["", Validators.required]
    })
});

children.component.html

<div [formGroup]="dummyGroup">
    <label>Dummy Input: </label>
    <input formControlName="dummyInput">
</div>

children.component.ts

private dummyGroup: FormGroup;

Ich weiß, dass etwas mit dem Code nicht stimmt, aber ich bin wirklich in einer Straßensperre. Jede Hilfe wäre zu schätzen.

Vielen Dank.

13
Mese

Die Hauptidee ist, dass Sie die formGroup und formControls als Variablen behandeln müssen, hauptsächlich Javascript-Objekte und Arrays.

Also werde ich etwas Code einbringen, um meinen Standpunkt darzulegen. Der folgende Code ist etwas, was Sie haben. Das Formular wird dynamisch erstellt, nur dass es in Abschnitte unterteilt ist, wobei jeder Abschnitt seinen Anteil an Feldern und Beschriftungen enthält.

Der HTML-Code wird durch TypeScript-Klassen gesichert. Das sind nicht hier, da sie nicht viel Besonderes haben. Nur die FormSchemaUI, FormSectionUI und FormFieldUI sind wichtig.

Behandeln Sie jeden Code als eigene Datei.

Bitte beachten Sie auch, dass formSchema: FormSchema ein JSON-Objekt ist, das ich von einem Dienst erhalte. Alle Eigenschaften der UI-Klassen, die nicht definiert sind, werden von ihren Basis-Datenklassen geerbt. Diese werden hier nicht dargestellt .. Die Hierarchie lautet: FormSchema enthält mehrere Abschnitte. Ein Abschnitt enthält mehrere Felder.

<form (ngSubmit)="onSubmit()" #ciRegisterForm="ngForm" [formGroup]="formSchemaUI.MainFormGroup">
    <button kendoButton (click)="onSubmit(ciRegisterForm)" [disabled]="!canSubmit()"> Register {{registerPageName}} </button>
    <br /><br />
    <app-ci-register-section *ngFor="let sectionUI of formSchemaUI.SectionsUI" [sectionUI]="sectionUI">
    </app-ci-register-section>
    <button kendoButton (click)="onSubmit(ciRegisterForm)" [disabled]="!canSubmit()"> Register {{registerPageName}} </button>
</form>

==============================================

<div class="row" [formGroup]="sectionUI.MainFormGroup">
    <div class="col-md-12  col-lg-12" [formGroupName]="sectionUI.SectionDisplayId">
        <fieldset class="section-border">
            <legend class="section-border">{{sectionUI.Title}}</legend>
            <ng-container *ngFor='let fieldUI of sectionUI.FieldsUI; let i=index; let even = even;'>
                <div class="row" *ngIf="even">
                    <ng-container>
                        <div class="col-md-6  col-lg-6" app-ci-field-label-Tuple [fieldUI]="fieldUI">

                        </div>
                    </ng-container>
                    <ng-container *ngIf="sectionUI.Fields[i+1]">
                        <div class="col-md-6  col-lg-6" app-ci-field-label-Tuple [fieldUI]="sectionUI.FieldsUI[i+1]">

                        </div>
                    </ng-container>
                </div>
            </ng-container>           
        </fieldset>
    </div>
</div>

==============================================

{{fieldUI.Label}}

==============================================

<ng-container>
    <div class="row">
        <div class="col-md-4 col-lg-4 text-right">
            <label for="{{fieldUI.FieldDisplayId}}"> {{fieldUI.Label}} </label>
        </div>
        <div class="col-md-8 col-lg-8">
            <div app-ci-field-edit [fieldUI]="fieldUI" ></div>
        </div>
    </div>       
</ng-container>

==============================================

<ng-container [formGroup]="fieldUI.ParentSectionFormGroup">    
    <ng-container *ngIf="fieldUI.isEnabled">         
        <ng-container [ngSwitch]="fieldUI.ColumnType">            
            <input *ngSwitchCase="'HIDDEN'" type="hidden" id="{{fieldUI.FieldDisplayId}}" [value]="fieldUI.Value" />
            <ci-field-textbox *ngSwitchDefault
                              [fieldUI]="fieldUI"
                              (valueChange)="onValueChange($event)"
                              class="fullWidth" style="width:100%">
            </ci-field-textbox>
        </ng-container>       
    </ng-container>
</ng-container>

==============================================

export class FormSchemaUI extends FormSchema { 

    SectionsUI: Array<FormSectionUI>;
    MainFormGroup: FormGroup;

    static fromFormSchemaData(formSchema: FormSchema): FormSchemaUI {
        let formSchemaUI = new FormSchemaUI(formSchema);
        formSchemaUI.SectionsUI = new Array<FormSectionUI>();
        formSchemaUI.Sections.forEach(section => {
            let formSectionUI = FormSectionUI.fromFormSectionData(section);

            formSchemaUI.SectionsUI.Push(formSectionUI);
        });
        formSchemaUI.MainFormGroup = FormSchemaUI.buildMainFormGroup(formSchemaUI);        
        return formSchemaUI;
    }

    static buildMainFormGroup(formSchemaUI: FormSchemaUI): FormGroup {
        let obj = {};
        formSchemaUI.SectionsUI.forEach(sectionUI => {
            obj[sectionUI.SectionDisplayId] = sectionUI.SectionFormGroup;
        });
        let sectionFormGroup = new FormGroup(obj);
        return sectionFormGroup;
    }
}

==============================================

export class FormSectionUI extends FormSection {

    constructor(formSection: FormSection) {        
        this.SectionDisplayId = 'section' + this.SectionId.toString();
    }

    SectionDisplayId: string;
    FieldsUI: Array<FormFieldUI>;
    HiddenFieldsUI: Array<FormFieldUI>;
    SectionFormGroup: FormGroup;
    MainFormGroup: FormGroup;
    ParentFormSchemaUI: FormSchemaUI;

    static fromFormSectionData(formSection: FormSection): FormSectionUI {
        let formSectionUI = new FormSectionUI(formSection);
        formSectionUI.FieldsUI = new Array<FormFieldUI>();
        formSectionUI.HiddenFieldsUI = new Array<FormFieldUI>();
        formSectionUI.Fields.forEach(field => {
            let fieldUI = FormFieldUI.fromFormFieldData(field);
            if (fieldUI.ColumnType != 'HIDDEN')
                formSectionUI.FieldsUI.Push(fieldUI);
            else formSectionUI.HiddenFieldsUI.Push(fieldUI);
        });
        formSectionUI.SectionFormGroup = FormSectionUI.buildFormSectionFormGroup(formSectionUI);
        return formSectionUI;
    }

    static buildFormSectionFormGroup(formSectionUI: FormSectionUI): FormGroup {
        let obj = {};
        formSectionUI.FieldsUI.forEach(fieldUI => {
            obj[fieldUI.FieldDisplayId] = fieldUI.FieldFormControl;
        });
        let sectionFormGroup = new FormGroup(obj);
        return sectionFormGroup;
    }
}

==============================================

export class FormFieldUI extends FormField {    

    constructor(formField: FormField) {
    super();
    this.FieldDisplayId = 'field' + this.FieldId.toString();       

    this.ListItems = new Array<SelectListItem>();        
   }

    public FieldDisplayId: string;

    public FieldFormControl: FormControl;
    public ParentSectionFormGroup: FormGroup;
    public MainFormGroup: FormGroup;
    public ParentFormSectionUI: FormSectionUI;  

    public ValueChange: EventEmitter<any> = new EventEmitter<any>();    

    static buildFormControl(formFieldUI:FormFieldUI): FormControl {
        let nullValidator = Validators.nullValidator;

        let fieldKey: string = formFieldUI.FieldDisplayId; 
        let fieldValue: any;
        switch (formFieldUI.ColumnType) {            
            default:
                fieldValue = formFieldUI.Value;
                break;
        }
        let isDisabled = !formFieldUI.IsEnabled;
        let validatorsArray: ValidatorFn[] = new Array<ValidatorFn>();
        let asyncValidatorsArray: AsyncValidatorFn[] = new Array<AsyncValidatorFn>();

        let formControl = new FormControl({ value: fieldValue, disabled: isDisabled }, validatorsArray, asyncValidatorsArray);
        return formControl;
    }
}
1
Dragos Durlut

sie können Ihrer untergeordneten Komponente eine Eingabe hinzufügen, um die FormGroup an sie zu übergeben. Mit FormGroupName können Sie den Namen Ihrer FormGroup übergeben :)

children.component.ts

@Input('group');
private dummyGroup: FormGroup;

parent.component.html

<div [formGroup]="userForm" novalidate>
    <div>
        <label>User Id</label>
        <input formControlName="userId">
    </div>
    <div formGroupName="dummy">
        <dummy [group]="userForm.controls['dummy']"></dummy>
    </div>
</div>
10

Nicht lügen, ich weiß nicht, wie ich diesen Beitrag vorher nicht gefunden habe.

Angular 2: Formular mit untergeordneter Komponente

Die Lösung besteht darin, die untergeordnete Komponente an dieselbe formGroup zu binden, indem Sie die formGroup von den übergeordneten an die untergeordneten Elemente als Eingabe übergeben.

Wenn jemand einen Code teilt, um das Problem auf andere Weise zu lösen, akzeptiere ich ihn gerne.

3
Mese

Um einen Verweis auf das übergeordnete Formular zu erhalten, verwenden Sie einfach dieses (möglicherweise nicht in Angular 2 verfügbar. Ich habe es mit Angular 6 getestet):

TS

import {
   FormGroup,
   ControlContainer,
   FormGroupDirective,
} from "@angular/forms";

@Component({
  selector: "app-leveltwo",
  templateUrl: "./leveltwo.component.html",
  styleUrls: ["./leveltwo.component.sass"],
  viewProviders: [
    {
      provide: ControlContainer,
      useExisting: FormGroupDirective
    }
  ]
})
export class NestedLevelComponent implements OnInit {
  //form: FormGroup;

  constructor(private parent: FormGroupDirective) {
     //this.form = form;
  }
}

HTML

<input type="text" formControlName="test" />
1
blacksheep_2011
import { Directive } from '@angular/core';
import { ControlContainer, NgForm } from '../../../node_modules/@angular/forms';

@Directive({
  selector: '[ParentProvider]',
  providers: [
    {
    provide: ControlContainer,
    useFactory: function (form: NgForm) {
      return form;
    },
    deps: [NgForm]
    }`enter code here`
  ]
})
export class ParentProviderDirective {

  constructor() { }

}
<div ParentProvider >
  for child
</div>

Eine Alternative zu FormGroupDirective (wie in @ blacksheeps Antwort beschrieben) ist die Verwendung von ControlContainer wie folgt:

import { FormGroup, ControlContainer } from "@angular/forms";

export class ChildComponent implements OnInit {

  formGroup: FormGroup;

  constructor(private controlContainer: ControlContainer) {}

  ngOnInit() {
    this.formGroup = <FormGroup>this.controlContainer.control;
  }

Die formGroup kann im direkten Elternteil oder weiter oben gesetzt werden (z. B. des Elternteils). Dadurch ist es möglich, eine from-Gruppe über verschiedene verschachtelte Komponenten zu übergeben, ohne dass eine Kette von @Input()s für die Weitergabe der formGroup erforderlich ist. Legen Sie in einem übergeordneten Element die Variable formGroup fest, um es über ControlContainer im untergeordneten Element verfügbar zu machen:

<... [formGroup]="myFormGroup">
0
Markus Pscheidt