0

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>
Jonathan Chaplin
  • 2,442
  • 1
  • 18
  • 21
  • i m not demotivating you but you have over complexed this thing – Harkal Sep 27 '20 at 15:33
  • it could be done with an easy approach – Harkal Sep 27 '20 at 15:33
  • i tried improving your code but its not easy let me give you easy appraoch – Harkal Sep 27 '20 at 15:34
  • 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 Sep 27 '20 at 15:36
  • I understand what you're saying, just curious why this doesn't work. – Jonathan Chaplin Sep 27 '20 at 15:45
  • you are doing 'ngFor' over an array of ChildComponent and moreover the directive has been used over 'app-child' so first mistake is the array must not hold the ChildComponent instead a data object and then the data object should be passed in @Input and when you get that object in Input in your child component then you directly set values to it – Harkal Sep 27 '20 at 15:53

2 Answers2

1

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.

Dharman
  • 30,962
  • 25
  • 85
  • 135
Harkal
  • 1,770
  • 12
  • 28
1

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>()
  }
}