Here is my problem.
Online Example of the issue
I have a dynamic JSON that I need to convert to a form. So, I used reactive forms and by going through all the properties of the JSON I create either a FormGroup or FormControl, in this way:
sampleJson ={prop1:"value1", prop2: "value2",...}
...
myForm: FormGroup;
myKeys=[];
...
ngOnInit() {
this.myForm = this.getFormGroupControls(this.sampleJson, this.myKeys);
}
getFormGroupControls(json:any,keys): FormGroup{
let controls = {};
let value = {};
for (let key in json) {
if (json.hasOwnProperty(key)) {
value = json[key];
if (value instanceof Object && value.constructor === Object) {
keys.push({"key":key,children:[]});
controls[key] = this.getFormGroupControls(value,keys[keys.length-1].children);
} else {
keys.push({"key":key,children:[]});
controls[key] = new FormControl(value);
}
}
}
return new FormGroup(controls);
}
After doing so, I use recursive templates to build the form, if I do not use recursive templates I get the form to work. However with recursive templates I am getting errors:
<form [formGroup]="myForm">
<div class="form-group">
<ng-template #nodeTemplateRef let-node>
<div class="node">
<div *ngIf="node.children.length">
{{"section [formGroupName]="}} {{ getNodeKey(node) }}
<section style="display:block;margin:20px;border:solid 1px blue;padding-bottom: 5px;"
[formGroupName]="getNodeKey(node)" >
<h1>{{ node.key }}</h1>
<ng-template
ngFor
[ngForOf]="node.children"
[ngForTemplate]="nodeTemplateRef">
</ng-template>
</section>
{{"end of section"}}
</div>
<div *ngIf="!node.children.length">
<label [for]="node.key">{{node.key}}</label>
<input type="text" [id]="node.key"
class="form-control">
</div>
</div>
</ng-template>
<ng-template *ngFor="let myKey of myKeys"
[ngTemplateOutlet]="nodeTemplateRef"
[ngTemplateOutletContext]="{ $implicit: myKey }">
</ng-template>
</div>
FormerComponent.html:25 ERROR Error: Cannot find control with name: 'road'
That corresponds to this sample JSON:
"address": {
"town": "townington",
"county": "Shireshire",
"road": {
"number": "1",
"street": "the street"
}
I have is being displayed, so I know the elements are there. What am I missing?
Template Driven Forms are based only on template directives, while Reactive forms are defined programmatically at the level of the component class. Reactive Forms are a better default choice for new applications, as they are more powerful and easier to use.
You can use [(ngModel)] with Reactive forms. This will a completely different directive than the one that would be used without the formControlName . With reactive forms, it will be the FormControlNameDirective . Without the formControlName , the NgModel directive would be used.
In Angular, a reactive form is a FormGroup that is made up of FormControls. The FormBuilder is the class that is used to create both FormGroups and FormControls.
A reactive dynamic forms application, which allows you to define forms containing some dynamic fields of different types (Text, Number, Checkbox, Date, DateTime, Time, Dropdown, Multiple Tags, Radio Group), then you can use these forms in any reactive/mobile screen. Which holds the entities (Form, Section, Field, Option, FieldData, FieldType).
Broadly speaking, with template-driven forms we declare our form model by writing plain-old-fashioned HTML forms and let Angular create a form model out of the DOM. Now, in reactive forms we write that form model in code and then bind instances of FormGroup and FormControl into the DOM.
A form model can be passed from one component to another using input properties. This technique allows building rather sophisticated and complex forms who are composed of several components. Reactive forms are a good fit for that idea, making it relatively easy to build nested forms.
Dynamic Reactive Forms App Component The app component defines the form fields and validators for the dynamic form using an Angular FormBuilder to create an instance of a FormGroup that is stored in the dynamicForm property. The dynamicForm is then bound to the <form> element in the app template below using the [formGroup] directive.
Problem with your current code seems to be that ng-template parent is your app component, so it doesnt take into account other formGroupNames in top templates you defined and always seek in root FormGroup.
It also seems that full group name/control name is not supported in templates (e.g. cant use formGroupName="address.road"
)
If you need for some reason formGroups -- you can pass them in context to templates. Or you can address formControls directly:
formGroupName
from template keys.push({"key":key,children:[], fullKey: parent ? parent.fullKey + '.' + key: key});
(you can store FormControl
instance itself as well ofc.)<input type="text" [formControl]="myForm.get(node.fullKey)"
Stackblitz Example
Or if you still want form groups / controls hierarchy you can use formGroup and formControl directives by passing them recursively (instead of formGroupName and formControlName)
Stackblits link
NB : same issue here : Recursive ReactiveForm cannot find formGroups inside of template
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With