0

I am working on an Angular component that manages a list of FormGroups. This list can be expanded and items can be deleted. But I also want the component to handle any kind of FormGroup. So for example, the FormGroup can contain 1 FormControl like so:

interface TestForm { color: FormControl<string | null>; } 

But it should also be able to handle a FormGroup with multiple FormControls, like this:

interface TestForm { color: FormControl<string | null>; nickName: FormControl<string | null>; hasPet: FormControl<boolean | null>; } 

Additionally, I want the component to handle a TemplateRef defined in the parent, which should contain all the form elements like so:

<ng-template #itemTemplate let-item="item"> <input type="text" [formControl]="item.controls.color" /> </ng-template> 

I created an example here of what it should look like: https://stackblitz.com/edit/nuurvgjk?file=src%2Fapp%2Feditable-list.component.ts

So in this example I use form elements because I know what the interface of the FormGroup looks like, but this should be dynamic and declared in an <ng-template> outside of this component. I got this working locally, but I consider it less relevant for the goal that I want to achieve.

I have played around with giving the FormGroup and the component a generic <T> type and tried to create a new FormGroup using the generic type. But I haven't found a way yet to actually do it.

I was hoping there was a way to do something like this:

interface SmallForm { a: number; b: string; } const formGroups: FormGroup<SmallForm>[] = [ new FormGroup({ a: new FormControl(2), b: new FormControl('test') }), new FormGroup({ a: new FormControl(11), b: new FormControl('hello') }) ]; const newFormGroup = new FormGroup<SmallForm>(); formGroups.push(newFormGroup); 

Maybe there is no way to set up a new FormGroup without knowing the interface beforehand, but I really want to find a conclusive answer.

9
  • I am sorry, this is very vague I could not understand what was the final goal or what you wanted exactly? Could you please include maybe example of how you'd want the final product be used like? Or detail more what you are trying to accomplish? At this point it just looks like you want to use a component to apply a loop... Commented Jul 2 at 10:00
  • @Salketer Thanks for the comment. You're right, it might be better to provide something of an example of what I am aiming for. I added some additional code of what I want. I don't think the example I provided will work, but hopefully it will make things a bit clearer. Commented Jul 2 at 10:15
  • Hmmm, look like you are looking for untyped forms: UntypedFormGroup? Commented Jul 2 at 10:18
  • 1
    @YongShun Unfortunately, that's not it. :( I tried that as well, but it just gives me an empty group without any FormControls. I would like some kind of group with default values to any FormControls and add that as a new item. Commented Jul 2 at 10:23
  • I was thinking about maybe creating an input to the component to provide a default FormGroup object and use that every time the user adds a new item to the list. But it would be way nicer if I could generate a FormGroup dynamically without having to provide an 'example' object. Commented Jul 2 at 10:25

1 Answer 1

1

Really I can see the whole question (I don't know where you define the forms or how pass the templates), so a few ideas related to "generic" FormGroup Array.

Basically you can use {[key:string]:any}

e.g. you can define your array of formGroups like

 forms: FormGroup<{[key:string]: any}>[] = [ new FormGroup<{[key:string]: any}>({ a: new FormControl('a'), }), new FormGroup<{[key:string]: any}>({ b: new FormControl('b'), c: new FormControl('c'), }) ]; 

And you can to have a component like

@Component({ selector: 'app-editable-list', template: `<p><strong>Editable list</strong></p> <ul> <li> <ng-container [ngTemplateOutlet]="fragment()||fool" [ngTemplateOutletContext]="{item: form()}"></ng-container> </li> </ul> <ng-template #fool>not template defined</ng-template> `, imports: [CommonModule, ReactiveFormsModule], }) export class EditableList { fragment = input<TemplateRef<unknown>|undefined>(); form=input<FormGroup<{[key:string]:any}>>(); } 

And use as

 <app-editable-list [fragment]="item1" [form]="forms[0]"/> <app-editable-list [fragment]="item2" [form]="forms[1]"/> <ng-template #item1 let-item="item"> {{item.value|json}} <input type="text" [formControl]="item.controls.a" /> </ng-template> <ng-template #item2 let-item="item"> <input type="text" [formControl]="item.controls.b" /> <input type="text" [formControl]="item.controls.c"/> </ng-template> 

You can see a stackblitz

NOTE: I hope you understand the idea, I imagine you can get the templates using ViewChildren and pass the QueryList to a component

Sign up to request clarification or add additional context in comments.

1 Comment

Hi! Thanks a lot for your thoughts and examples! I do understand what you're saying. How would you write the feature to add a new item from within the editable-list component?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.