0
votes

I'm creating components in an ngFor and wondering why they don't update properly. Here is a stackblitz: https://stackblitz.com/edit/angular-ivy-naoejz?file=src%2Findex.html

Basically, when I update and tab out of the child components I add then submit the form the values are blank and I'd like to know why?

I know I could use FormBuilder, FormGroup and FormArray like this post: angular material stepper add new step items dynamically on every click , but I'm curious why what I'm doing doesn't work.

app.component.html
....

  <app-child *ngFor="let child of childArray;let i = index;" [index]=i [childArray]=childArray>
  </app-child>

  <button type="button" (click)="addChildComponent()" > Add</button>

app.component.ts
....

export class AppComponent {
  title = 'ng-example';
  childArray: Array<ChildComponent>;

  fields = {
    myName : {
      value:'testName'
    },
    myDesc: {
      value:'testDesc'
    }
  };

  addChildComponent(){
    this.childArray.push(new ChildComponent());
  }

  onSubmit() {
    const formData: any = new Object();

    const childData = [];
    console.log(this.childArray.length);
    this.childArray.forEach((child) => {
      const { myValue } = child.fields;
      childData.push({
        'childVal': myValue.value
      });
    });
    formData.childData = childData;
    //childData array has objects with childVal = '', why??
  }

  ngOnInit() {
    this.childArray = new Array<ChildComponent>()
  }

child.component.ts
....

export class ChildComponent {
    @Input() index : number;

    fields = {
        myValue : {
          value:''
        }
    };

    inputBlur(event, fieldName) {
        this.fields[`${fieldName}`].value = event.target.value;       
    }
}

child.component.html ....

<div>
    <input name="myInfo{{index}}" (focusout)="inputBlur($event, 'myValue')" />
</div>
2
i m not demotivating you but you have over complexed this thing - Harkal
it could be done with an easy approach - Harkal
i tried improving your code but its not easy let me give you easy appraoch - Harkal
in your child component receive an object directly through @Input and put the value in it and the value should be changed in (input) callback instead of (inputBlur) and loop over an array which has the objects of type you want the data to be and on add button click just push an empty object to the array. if you want i can write some pseudo code for you - Harkal
I understand what you're saying, just curious why this doesn't work. - Jonathan Chaplin

2 Answers

1
votes

I'm writing some pseudo-code just for your easy understanding

app.component.ts

// the custom field object
class Field {
   id?: string;
   value?: string;
}

class AppComponent {
    // this is the fields array
    fields: Field[] = [
        {
           id: "myName",
           value: "Test Name"
        },
        {
           id: "myDesc",
           value: "Test Desc"
        }
    ];

    onSubmitClick() {
       console.log(this.fields); // here you ll get what you want 
    }


}

app.component.html

<app-child ngFor="let field of fields" [field]="field"></app-child>
<button (click)="fields.push({})">add</button>
<button (click)="onSubmitClick()">submit</button>

child.component.ts

class ChildComponent {
    @Input('field') field: Field;
}

child.component.html

<input #input type="text" (input)="field.value = input.value" />

I haven't tested the code but it'll work for what you want.

1
votes

You don't have access to the changes in fields of Children components. You need to use @ViewChildren and QueryList for this.

import { Component, QueryList,  ViewChild, ViewChildren } from '@angular/core';
import { ChildComponent } from './child/child.component';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'ng-example';
  childArray: Array<ChildComponent>;
  @ViewChildren(ChildComponent) children1: QueryList<ChildComponent>;

  fields = {
    myName : {
      value:'testName'
    },
    myDesc: {
      value:'testDesc'
    }
  };

  inputBlur(event, fieldName) {
    this.fields[`${fieldName}`].value = event.target.value;
  }

  addChildComponent(){
    this.childArray.push(new ChildComponent());
  }

  onSubmit() {
    const formData: any = new Object();

    const childData = [];
    this.children1.toArray().forEach((child) => {
      const { myValue } = child.fields;
      childData.push({
        'childVal': myValue.value
      });
    });
    formData.childData = childData;

    console.log(formData);
  }

  ngOnInit() {
    this.childArray = new Array<ChildComponent>()
  }
}