Search code examples
angularangular-ngmodelangular-forms

How to get checked values into an array for Angular Template Driven Form (ngModel)


I'm attempting to use template driven forms, binding to a model in the html to a new instance of lets say Person. I've been unsuccessful in creating a proper binding for checkboxes to a single array property on my model.

The idea is data will come from an api or other source, dynamically render checkboxes via *ngFor and bind what is selected to the Person models property which would be an array of numbers. For example:

class Person {
  firstName: string;
  someCheckboxPropList: number[];
}

and data could be anything really

const dynamicData = [
  { name: 'name1', value: 1 },
  { name: 'name2', value: 2 }
];

my expected output would be something along the lines of [ 1, 2 ] if both values where to be checked and if only the second was checked [ 2 ].

Here's a sample of what the PersonComponent.ts file might look like

@Component({ ... })
export class PersonComponent {

    submitted = false;

    model = new Person();

    constructor() { }

    onSubmit(form) {
        this.submitted = true;
        console.log(form);
    }

}

And where I'm at with the components html file.

<form (ngSubmit)="onSubmit(form)" #form="ngForm">

    <input type="text" [(ngModel)] name="person.firstName">

    <div *ngFor="let dataItem of dynamicData" >
        <input 
            type="checkbox"
            ngModel
            name="dynamicData"
            [value]="dataItem.value">
        <label>{{dataItem.name}}</label>
    </div>

</form>

This does not work (and is sample code anyway).


Solution

  • the idea is have two things: Person and PersonForm, so,e.g

    person={firstName:"Jessy",props:[1,2]
    //but 
    personForm={firstName:"Jessy",props:[true,true]
    

    So, make two functions

      createForm(person) {
        return {
          firstName: person.firstName,
          props: this.dynamicData.map(x => person.props.indexOf(x.value) >= 0)
        }
      }
      retrieveData(personForm) {
        let props: number[] = [];
        personForm.props.forEach((v, index) => {
          if (v)
            props.push(this.dynamicData[index].value)
        }
        )
        return {
          firstName: personForm.firstName,
          props: props
        }
      }
    

    Well, we have already all we need. When we received a person, create a personForm that it's the data we change in the form. In submit simply call to retrieveData to get the value of person.

    When we have a person create a personForm,e.g.

    this.service.getPerson().subscribe(person=>
        {
           this.personForm=createForm(person)
        }
    )
    

    Our form

    <form *ngIf="personForm" (submit)="sendData(personForm)">
      <input name="firtName" [(ngModel)]="personForm.firstName">
      <div *ngFor="let item of dynamicData;let i=index">
      <input name="{{'prop'+i}}" 
             type="checkBox" [(ngModel)]="personForm.props[i]">{{item.name}}
      </div>
      <button>Submit</button>
    </form>
    {{personForm|json}}<br/>
    {{retrieveData(personForm)|json}}
    

    And our sendData function

    sendData(personForm)
    {
        console.log(this.retrieveData(personForm))
    }
    

    I make a simple stackblitz

    Update

    NOTE:We can use the spred operator to asing properties, so

      createForm(person) {
        return {
          ...person, //All the properties of person, but
          props: this.dynamicData.map(x => person.props.indexOf(x.value) >= 0)
        }
      }
      retrieveData(personForm) {
        let props: number[] = [];
        personForm.props.forEach((v, index) => {
          if (v)
            props.push(this.dynamicData[index].value)
        }
        )
        return {
          ..personForm, //all the properties of personForm, but
          props: props
        }
      }
    

    NOTE2: In a "real world" the persons goes from a service. Consider the idea that service get/received the "personForm" and put the functions to transform in the service

    //in service
    getPerson()
    {
        return this.httpClient.get("...").map(res=>this.createForm(res))
    }
    savePerson(personForm)
    {
        return this.httpClient.post("...",this.retrieveData(personForm))
    }