Search code examples
htmlangulartypescriptangular-reactive-formsangular-forms

How to bind a formArray from parent component to child component and be able to change the values in the formArray using Angular reactive forms?


I have a parent component with a reactive form including controls and a from group.

When choosing playerType dropdown I am adding to the fromGroup a formArray. The new form array will consits of 2 form groups or 3 depending on playerType.

Let say a user created a Player and choose playerType = "Mage", then I need to create a new "skills" formArray inside configuration formGroup.

Example below - base form:

public PlayerForm: PlayerFormGroup = this.fb.group(
    {
        id: null,
        name: null, 
        life: 100, 
        playerType: null,
        configuration: this.fb.group({
            playerAllies: null
            weapons: []
        })
    
) as PlayerFormGroup;

enter image description here

So now the player is created and new formArray is added.

Look like this:

  public PlayerForm: PlayerFormGroup = this.fb.group(
    {
        id: 1,
        name: UserMage1, 
        life: 100, 
        playerType: 2,
        configuration: this.fb.group({
            playerAllies: null
            weapons: [],
            skills: [mageSkills:[name:'', power:null, duration:0]]
        })
    
) as PlayerFormGroup;

-> It will look like:

id:1,

name: 'UserMage1',

life:100,

playerType: 2,

configuration:

[playerAllies: null,

weapons:[],

skills:mageSkills:[0:{ name: 'someName', power:'fire', duration:2 }, 1:{ name: 'teleport', power:'dark', duration:4 }] ]

enter image description here

So, I am having trouble on saving the Power and Duration.

Code:

Parent TS:

public getSkillDetails(skillName: string): SkillView{
  // from child-component-configure, on selecting get the selected skill name and pass 
  // it to child-component-view-Skill-details (using event emitter)
    return this.skillsArray.find((skill) => skill.name === skillName);
  }

Parent HTML:

<parent-component [formGroup]="PlayerForm" (submit)="playerForm.submit()">
      <mat-form-field>
        <mat-label>"Name"</mat-label>
        <input matInput formControlName="name">        
      </mat-form-field>
     
     <mat-form-field>
        <mat-label>"Life"</mat-label>
        <input matInput formControlName="life">        
      </mat-form-field>

    <mat-form-field>
        <mat-label>"Type"</mat-label>
        <input matInput formControlName="playerType">        
      </mat-form-field>

  <child-component-configure *ngIf="PlayerForm.control.playerType.value != null"
      [selectedSkill] = "skill"> (event emitter to pass selected skill name)
  </child-component-configure>

 <child-component-view-Skill-details [skillSelected] = "skill"> 
 </child-component-view-Skill-details [value]="getSkillDetails(this.selectedSkill)">
</parent-component>

Child-HTML:

<child-component-configure>
  <mat-form-field>
        <mat-label>"Allies"</mat-label>
        <input matInput formControlName="allies">        
   </mat-form-field>

 <mat-form-field>
        <mat-label>"Weapons"</mat-label>
        <input matInput formControlName="weapons">        
  </mat-form-field>

 <mat-form-field>
        <mat-label>"Weapons"</mat-label>
        <input matInput formControlName="weapons">        
  </mat-form-field>

 </child-component-configure>

Child-view-Skill-details-HTML:

     <child-component-view-Skill-details> 
         <div>
          <label>Power</label>
          <input formControlName="power">        
         </div>

         <div class="slider-text">
           <label>Duration</label>
           <mat-slider
            [max]="10"
            [min]="1"
            [step]="1"
            [(ngModel)]="value.timeout">
          </mat-slider>
    </div>
    </child-component-view-Skill-details>
  • Diagram demonstrating the separation of components and the uses of form controls:

enter image description here

Above code is an abstraction of the code.

I want to find a way to change duration and power and update/pass the values to base form.

Currently creating is ok but, on updating Power and Timeout are always null and 0.

If there is any more info I can provide please let me know.


Solution

  • if you want update formGroup, you can do like this

    your code

    public PlayerForm: PlayerFormGroup = this.fb.group(
        {
            id: 1,
            name: UserMage1, 
            life: 100, 
            playerType: 2,
            configuration: this.fb.group({
                playerAllies: null
                weapons: [],
                skills: [mageSkills:[name:'', power:null, duration:0]]
            })
        
    ) as PlayerFormGroup;
    

    this works!

    PlayerForm = this.fb.group({
      id: new FormControl(1),
      name: new FormControl(userMage1),
      life: new FormControl(100),
      playerType: new FormControl(2),
      configuration: new FormGroup({
        playerAllies: new FormControl(null),
        weapons: new FormArray([]),
        skills: new FormArray([
          new FormGroup({
            mageSkills: new FormArray([new FormGroup({
              name: new FormControl(''),
              power: new FormControl(null),
              duration: new FormControl(0)
            })])
          })
        ])
      })
    })
    
    updatePlayer() {
      const form = this.PlayerForm.get('configuration').get('skills') as FormArray;
      const formArr = form.controls[0].get('mageSkills') as FormArray;
      const formMageSkills = formArr.controls[0]
      formMageSkills.get('name').setValue('someName')
      formMageSkills.get('power').setValue('fire')
      formMageSkills.get('duration').setValue(2)
    }