1
votes

I want to build a dynamic form component that can take in an object and generates a new form. I'm using Angular RC6

Example of the data:

[{
    formgrouplabel: "registration",
    data: [{
        type: "checkbox",
        label: "checkbox 1",
        name: "checkbox1",
        default: false,
        required: false
    }, {
        type: "radio",
        label: "mr",
        name: "sex",
        default: true,
        required: true
    }, {
        type: "radio",
        label: "miss",
        name: "sex",
        default: false,
        required: true
    }]
}, {
    formgrouplabel: "some other part of the form",
    data: [another array with input objects]
}]

The actual object will include much more information like validators and other requirements. I would also like to bundle inputs into groups.

I built the corresponding angular components:

<dynamic-form></dynamic-form>
<dynamic-form-group></dynamic-form-group>
<dynamic-checkbox></dynamic-checkbox>
<dynamic-radiobutton></dynamic-radiobutton>

In my template of dynamic-form-group I *ngFor over all formgroups in the data object. ngSwitchCase's are then used to pass a checkbox object to the dynamic-checkbox component and pass a radiobutton object to the the dynamic radio-button componenent.

All of this is working just fine now, the whole form is generated, However:

When I now use ngModel inside the dynamic-checkbox componenet for example to tie a FormControl to an input element, ngModel is not able to communicate with ngForm on the dynamic-form component (two levels up). Also FormGroup inside the dynamic-form-group component is not able to communicate with ngForm. In short, nothing is allowed to communicate from child to parant.

I'm reading ng-book 2 and all the examples on the angular docs, yet I can't find a clean solution for this. I know that if you want a child element to communicate with it's parent you either create a service or export a certain value/object or function through @Export and event emitters. This however doesn't lead to a clean solution.

The problem as I understand it now is when you use FormBuilder or FormGroup and FormControl you have to instantiate the controls first: this.form = new FormGroup({checkboxControl: new FormControl("some value")});

or (as written in ng book 2)

myForm: FormGroup;
constructor(fb: FormBuilder) { this.myForm = fb.group({
      'sku': ['ABC123']
    });
}

Both of these are not really an option for me. Because of the object model my form will be created first, next my formgroups and last my formcontrols. In order to use the above methods it should be the other way around.

Is there a way to create a form group first and add controlls later? I tried this already:

this.form = new FormGroup({});
this.form.controls['newControl'] = new FormControl({"some value"});

This does add a new formControl to my existing FormGroup, however when the value on that input changes, the formControl doesn't update. It's like I only made a reference to "new FormControl({"some value"});" and the value always stays at "some value".

Lastly I also tried this approach: https://angular.io/docs/ts/latest/cookbook/dynamic-form.html

Here too they first build the formcontrols and then pass them as an object to create a formgroup.

Of course I can just put all template and control logic into the dynamic-form component and leave out the dynamic-form-group, dynamic-checkbox and other components, but is there any way I can make this work as I intended ? I hope someone has already built something complex with the new forms api.

1
I've made a proper answer about that here stackoverflow.com/a/56375605/2398593 there's a demo on stackblitz too. Check out the library to help you do that here: github.com/cloudnc/ngx-sub-formmaxime1992

1 Answers

1
votes

Take a look at the addControl() feature on FormGroup. There are quite a few handy built-in methods on the FormGroup, FormArray, FormControl etc. for a variety of scenarios.

Example of use

/* 
    basic-form-question.component.ts -> From Angular Dynamic Form Documentation 
*/
@Component({
   selector: 'df-question',
   templateUrl: './basic-form-question.component.html'
})
export class DynamicFormQuestionComponent implements OnInit {
    @Input() question: QuestionBase<any>;
    @Input() form: FormGroup;

    ngOnInit() {
        if(!this.form.controls.hasOwnProperty(this.question.key)){
            let control = new FormControl(this.question.value || '', Validators.required);

            this.form.addControl(this.question.key, control);
        }
    }
}

Hope this helps.