4

I'm learning reactive forms still, and I want to have multiple checkboxes for a property that is array (e.g. hobby). So I defined the group as follows

this.myForm = this.fb.group({

    name: ['', Validators.required],

    hobbies: this.fb.array([])

});

Hobbies is an array: hobbies = ['Cooking', 'Swimming', 'Hiking', 'Dancing'];

Now, in HTML, I defined the checkboxes as follows:

<div>
  <span *ngFor="let hobby of hobbies">
    <label>
      <input type="checkbox" [value]="hobby" (click)="addToHobby(hobby)"> {{hobby}}
    </label>  
    <br />
  </span>
</div>

Now when checking an element it doesn't add it to the hobbies property at all, it just duplicates the actual HTML element. What am I doing wrong?

Here's the code: https://plnkr.co/edit/j8V189VoWKvloNgwOr7p?p=preview

7
  • plnkr.co/edit/JKVWAQgmp4RfhK72fIdp?p=preview Commented Mar 27, 2017 at 16:06
  • Well , when the user selects a checkbox, I want it to be visible in the {{myForm.value | json}} and when unselects as well. Also, when setting a model, the checkboxes should be selected accordingly to the user's properties. Commented Mar 27, 2017 at 16:06
  • If you want to unselect why you always push it? Commented Mar 27, 2017 at 16:06
  • in the example it gets pushed to array even when unselecting. what would be the correct approach? Commented Mar 27, 2017 at 16:07
  • 1
    This isn't really the "reactive forms" way of doing things. You shouldn't have to handle events to update the form. Commented Mar 27, 2017 at 18:39

1 Answer 1

4

OK I managed to get something working. The link is available below, but firstly, here's my chain of thought.

Firstly, we need to just define an array of all values, possibly we get this from a web service, but still, I mock them like this:

hobbies = [new Hobby(1, 'Cooking'), new Hobby(2, 'Swimming'), new Hobby(3, 'Hiking'), new Hobby(4, 'Dancing')]; I purposely added id as well, so it will mimic the real world more accurately. Now, we need to define the form. Since the multi checkboxes is not going to work, I decided to represent an array of individual checkboxes, each having several attributes, but at least two are necessary: the boolean to decide if that checkbox in array was selected and a name so we can know what we're selecting.

In order to achieve this, we need to firstly create an array that would fit the form, which is done as follows:

let hobbiesArray = [];

for (let hobby of this.hobbies) {
  hobbiesArray.push(this.fb.group({
    isChosen: false,
    name: hobby.hobbyName,
    id: hobby.id
  }));
}

As evident, I created new fb.group for each hobby and they're all placed in an array. This array is then used to create the form, as follows:

this.myForm = this.fb.group({
  hobbiesList: this.fb.array(hobbiesArray)
});

Now we have an fb.array of fb.groups. Now, when it comes to HTML, I have several concerns. The logic is "go through the myForm.hobbiesList and manipulate isChosen while keeping the name readonly - sort of. Here's the code.

<div formArrayName="hobbiesList">
  <div *ngFor="let hobby of hobbiesList.controls; let i=index" [formGroupName]="i" >
    <input type="checkbox" formControlName="isChosen" /> {{hobby.controls.name.value}}
  </div>
</div> 

Here's my concern: can I just address the name like this: {{hobby.controls.name.value}}? What harm can this do? Am I breaking any Angular2/4 rules?

OK, so, when confirming the selected hobbies, we get the actual array with name and ids (without the isChosen values) as follows:

submitMe(): void {
    let items = this.myForm.value;
    this.mySelectedHobbies = items.hobbiesList.filter(x => x.isChosen).map(x => { return { name: x.name, id: x.id }; });
}

So yeah, this is my design. I'm not sure if it's all allowed what I did but I find this the easiest way to solve the issue. What do you guys think?

Here's the code: https://plnkr.co/edit/8OKiQMK788R2uCt2OWzm?p=preview

Sign up to request clarification or add additional context in comments.

3 Comments

I was searching a lot to get the same behavior, finally I got the exactly same pattern as you describe here, at the momment it seens to be the way to go; at least to me doesn't seems to break any angular rule. (Just keep and eye when you call form.reset)
Thanks for your input! I think I managed to get it a little better. It's based on this solution: netbasal.com/… so after examining his approach, I could tweak mine a bit, so the final result is here: plnkr.co/edit/q4C576xJCRIFCVyMxDeX?p=preview I think it's a bit better then my original solution, because I don't address the name like this: {{hobby.controls.name.value}} but rather, like this: {{mockSkills[i].name}} I don't know which approach is better though. Any thoughts on all these approaches?
This is my final, alternative version with update / insert functionalities. Please, feel free to change / edit code, I'm sure there's a lot that can be improved. plnkr.co/edit/hmKVFOQZhajgAldJGPlb?p=preview

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.