如何在Angular 14中从数组创建树形表单?

huangapple go评论73阅读模式
英文:

How to create tree format form array in angular14?

问题

I have attached my code link. I tried to create a dynamic nested tree format from an array. But I don't know how to create it. I am new to the Angular framework.
<https://stackblitz.com/edit/angular-vytk36?file=src/index.html>

英文:

I have attached my code link. I tried create dynamic nested tree format form array. But I don't know how to create. I am new in angular framework.
<https://stackblitz.com/edit/angular-vytk36?file=src/index.html>

答案1

得分: 1

Here is the translation of the provided text:

"始终存在一个“无限级别”是制作一个“递归组件”的工作。

递归组件只是一个调用自身的组件(参见例如此SO)。

管理一个子组件,该子组件从父组件中管理一个formGroup并不容易。我们可以使用viewProviders或将FormGroup传递给输入项(请参见另一个SO)。

我们将选择传递formGroup。但由于我们的组件是递归组件,我们需要知道我们所在的formGroup,因此我使用另一个输入项,即formGroup的“名称”(请记住,我们的formArray是formGroup的formArray。我们需要“到达”formGroup)。

呼...!

在定义一个创建formGroup的函数之前,我们将导出它,因为同一个函数可以被父组件和组件使用。

export function setNestedMenu(data: any = null) {
  data = data || { name: "", displayname: "", id: "", nestedMenu: [] }
  return new FormGroup({
    name: new FormControl(data.name),
    displayName: new FormControl(data.name),
    id: new FormControl(data.name),
    nestedMenu: new FormArray(data.nestedMenu.map((x: any) => this.setNestedMenu(x)))
  })
}

注意:我使用驼峰命名法为“属性”赋值。

组件:

@Component({
  selector: 'form-component',
  standalone: true,
  imports: [CommonModule, FormsModule, ReactiveFormsModule],
  template: `
  <div [style.margin-left.px]="index*10" *ngIf="formGroup" [formGroup]="formGroup">
    <div formArrayName="nestedMenu">
       <button (click)="addMenu()">add</button>
       <div *ngFor="let group of menuArray?.controls;let i=index" [formGroupName]="i">
         <input formControlName="id"/>
         <input formControlName="name"/>
         <input formControlName="displayName"/>
         <button (click)="removeMenu(i)">remove</button>
         <button (click)="addSubMenu(i)">add submenu</button>
         <ng-container *ngIf="getSubMenuArray(i).length">
           <!-- here we call again to our component -->
            <form-component [form]="form" 
                            [name]="name+'.'+i+'.nestedMenu'" 
                            [index]="index+1"></form-component>
         </ng-container>
       </div>
    </div>
  </div>`,
})
export class FormMenuComponent  {
  _index: number = 0;
  formGroup: FormGroup
  @Input() form: FormGroup;
  @Input() name: string = "nestedMenu";
  @Input('index') set _(value: number) {
    this._index = value;
    this.formGroup = (value ? this.form.get(this.name).parent : this.form) as FormGroup
  }

  get index() {
    return this._index
  }
  get menuArray() {
    return this.form ? this.form.get(this.name) as FormArray : null;
  }
  getSubMenuArray(index: number) {
    return this.form.get(this.name + '.' + index + '.nestedMenu') as FormArray
  }

 addMenu() {
   this.menuArray.push(setNestedMenu())
 }
 addSubMenu(index: number) {
   this.getSubMenuArray(index).push(setNestedMenu())
 }
 removeMenu(index: number) {
  this.menuArray.removeAt(index)
 }
}

请注意如何传递名称、form和索引变量。

我们的main.component只需:

<form-component [form]="form" [index]="0"></form-component>
  
form: FormGroup = new FormGroup({
    nestedMenu: new FormArray([setNestedMenu()])
  })

请查看stackblitz

注意:对于自引用引文表示歉意。

If you need further assistance or have any questions, please let me know.

英文:

Always we have a "infinite level" is work to make a "recursive component"

A recursive component is only a component that call itself (see e.g. this SO)

Mannage a child component that mannage a formGroup from parent it's not very easy. We can use viewProviders or pass the FormGroup in an input (see this another SO)

We are going to choose pass the formGroup. But as our component is a recursive component we need in witch formGroup we are, so I use another input that it's the "name" of the formGroup. (remember that our formArrays are formArrays of formGroups. We need "reach" the formGroup

Puff...!

Before we are going to define a function to create the formGroup. We are export it because the same function can be used by the parent and by the component

export function setNestedMenu(data:any=null){
data=data || {name: &quot;&quot;,displayname: &quot;&quot;,id: &quot;&quot;,nestedMenu: []}
return new FormGroup({
name:new FormControl(data.name),
displayName:new FormControl(data.name),
id:new FormControl(data.name),
nestedMenu:new FormArray(data.nestedMenu.map((x:any)=&gt;this.setNestedMenu(x)))
})
}

NOTE: I use camelCase to give value to the "properties"

The component

@Component({
selector: &#39;form-component&#39;,
standalone: true,
imports: [CommonModule, FormsModule, ReactiveFormsModule],
template: `
&lt;div [style.margin-left.px]=&quot;index*10&quot; *ngIf=&quot;formGroup&quot; [formGroup]=&quot;formGroup&quot; &gt;
&lt;div formArrayName=&quot;nestedMenu&quot;&gt;
&lt;button (click)=&quot;addMenu()&quot;&gt;add&lt;/button&gt;
&lt;div *ngFor=&quot;let group of menuArray?.controls;let i=index&quot; 
[formGroupName]=&quot;i&quot;&gt;
&lt;input formControlName=&quot;id&quot;/&gt;
&lt;input formControlName=&quot;name&quot;/&gt;
&lt;input formControlName=&quot;displayName&quot;/&gt;
&lt;button (click)=&quot;removeMenu(i)&quot;&gt;remove&lt;/button&gt;
&lt;button (click)=&quot;addSubMenu(i)&quot;&gt;add submenu&lt;/button&gt;
&lt;ng-container *ngIf=&quot;getSubMenuArray(i).length&quot;&gt;
&lt;!--here we call again to our component--&gt;
&lt;form-component [form]=&quot;form&quot; 
[name]=&quot;name+&#39;.&#39;+i+&#39;.nestedMenu&#39;&quot; 
[index]=&quot;index+1&quot; &gt;&lt;/form-component&gt;
&lt;/ng-container&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;`,
})
export class FormMenuComponent  {
_index:number=0;
formGroup:FormGroup
@Input() form: FormGroup;
@Input() name:string=&quot;nestedMenu&quot;;
@Input(&#39;index&#39;) set _(value:number){
this._index=value;
this.formGroup=(value?this.form.get(this.name).parent:this.form) as FormGroup
}
get index()
{
return this._index
}
get menuArray() {
return this.form?this.form.get(this.name) as FormArray:null;
}
getSubMenuArray(index:number){
return this.form.get(this.name+&#39;.&#39;+index+&#39;.nestedMenu&#39;) as FormArray
}
addMenu()
{
this.menuArray.push(setNestedMenu())
}
addSubMenu(index:number)
{
this.getSubMenuArray(index).push(setNestedMenu())
}
removeMenu(index:number)
{
this.menuArray.removeAt(index)
}
}

See how pass the name, the form and the index variables

Our main.component simply

&lt;form-component [form]=&quot;form&quot; [index]=&quot;0&quot;&gt;&lt;/form-component&gt;
form: FormGroup=new FormGroup({
nestedMenu:new FormArray([setNestedMenu()])
})

See stackblitz

NOTE: sorry for self quoting

答案2

得分: 0

在表单之前传递给表单的两个变量是:“form” 和 “formArray”的名称。这会使组件变得复杂。有另一种更好的方法,那就是传递“formGroup”。

首先,我们的组件有一个像这样的“setter”输入:

formGroup: FormGroup
@Input('form') set __(value: AbstractControl) {
  this.formGroup = value as FormGroup
}

(需要对控件进行“类型转换”)

现在我们可以使用类似以下的内容:

<div *ngIf="formGroup" [formGroup]="formgroup">
  ...
  <div formArrayName="nestedMenu">
    <div *ngFor="let group of nestedMenuArray?.controls; let i=index" [formGroupName]="i">
      ...
      <ng-container *ngIf="getNestedMenuArray(i)?.length">
        <!--看,我们将“formGroup”传递为“form”-->
        <form-component [form]="nestedMenuArray.at(i)" [index]="index+1">
        </form-component>
      </ng-container>
    </div>
  </div>
</div>

get nestedMenuArray() {
  return this.formGroup ? this.formGroup.get('nestedMenu') as FormArray : null;
}

getNestedMenuArray(index: number) {
  return this.nestedMenuArray ? this.nestedMenuArray.at(index) as FormArray : null
}

stackblitz

英文:

The before answer pass to the form two variables: the "form" and the "name" of the formArray. This complicate the component. There're another better aproach that it's pass the "formGroup".

The first is that our component has an "setter" input like

  formGroup:FormGroup
@Input(&#39;form&#39;) set __(value:AbstractControl){
this.formGroup=value as FormGroup
}

(It's necessary to "cast" the control)

Now we can use some like

&lt;div *ngIf=&quot;formGroup&quot; [formGroup]=&quot;formgroup&quot;&gt;
...
&lt;div formArrayName=&quot;nestedMenu&quot;&gt;
&lt;div *ngFor=&quot;let group of nestedMenuArray?.controls;let i=index&quot;
[formGroupName]=&quot;i&quot;&gt;
...
&lt;ng-container *ngIf=&quot;getNestedMenuArray(i)?.length&quot;&gt;
&lt;!--see we pass as form the &quot;formGroup&quot;--&gt;
&lt;form-component [form]=&quot;nestedMenuArray.at(i)&quot;  [index]=&quot;index+1&quot; &gt;
&lt;/form-component&gt;
&lt;/ng-container&gt;
&lt;/div&gt;
&lt;/div&gt;
get nestedMenuArray() {
return this.formGroup?this.formGroup.get(&#39;nestedMenu&#39;) as FormArray:null;
}
getNestedMenuArray(index:number){
return  this.nestedMenuArray?this.nestedMenuArray.at(index) as FormArray:null
}

stackblitz

huangapple
  • 本文由 发表于 2023年4月19日 21:43:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/76055270.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定