35

I m using a checkbox in Angular to select more than one element and I'm trying to get the value of that checkbox for submitting.

Instead I'm getting the value those values as an array of true-s. That's what I've tried:

export class CreateSessionComponent implements OnInit {
  form : FormGroup ;

  constructor(private formBuilder: FormBuilder) {}

  ngOnInit() {
    this.form = this.formBuilder.group({
      userdata :  new FormArray([
        new FormControl('', Validators.required)
      ])
    })
  }
}

userdata is a dynamic array populated from a database.

<div formArrayName="useremail; let k = index">
  <div *ngFor="let data of userdata">
    <div>
      <input type="checkbox" name="useremail" formControlName ="{{k}}" [value]="data.email">{{data.email}}
    </div>
  </div>
</div> 
1
  • probably multiple checkboxes Commented Apr 15, 2017 at 8:21

7 Answers 7

68

Since you have several values you want to use, you need to use an FormArray, since FormControl can only capture one value.

Start with declaring an empty formArray:

this.myForm = this.fb.group({
  useremail: this.fb.array([])
});

Iterate the your emails, and watch for the change event and pass the respective email and event to a method onChange where you check if it's checked, then add the respective email to the formarray, if it's unchecked, remove the chosen email from the form array:

<div *ngFor="let data of emails">
  <input type="checkbox" (change)="onChange(data.email, $event.target.checked)"> {{data.email}}<br>
</div>

And onChange:

onChange(email:string, isChecked: boolean) {
  const emailFormArray = <FormArray>this.myForm.controls.useremail;

  if(isChecked) {
    emailFormArray.push(new FormControl(email));
  } else {
    let index = emailFormArray.controls.findIndex(x => x.value == email)
    emailFormArray.removeAt(index);
  }
}

Demo

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

7 Comments

if there are multiple values are checked then how can i get the multiple values above which gets the single checked value
data.email is a dynamic value which i will get from the database which contains multiple values
and what are these values? Where are the checkboxes, are they all email checkboxes? You have only one formcontrol, so you cannot capture more than one. You'd have to perhaps use some formarray/nested formgroup, but it's impossible to help further if you do not show more code.
from your answer in the plunker data = {email:"email value",email: "value2 "} contains two values and if both values are checked then how can i get those values on the form submit
Then you have to create a formarray instead of formcontrol for the emails.
|
6

The problem with the @AJT_82 answer is that if you want to modify the model using form.reset() or or form.patchValue(), it won't work. To resolve these problems you need to implement ControlValueAccessor interface. Basically you need to create 2 components: group component which holds the model value and implements ControlValueAccessor and checkbox component, the actual checkbox. I have written a blog post with detailed explanation as well as created a plunker which demonstrate some examples.

Final usage:

<checkbox-group [(ngModel)]="selectedItems">
    <checkbox value="item1">Item 1</checkbox>
    <checkbox value="item2">Item 2</checkbox>
    <checkbox value="item3">Item 3</checkbox>
    <checkbox value="item4">Item 4</checkbox>
</checkbox-group>

Implementation of group component:

@Component({
selector: 'checkbox-group',
template: `<ng-content></ng-content>`,
providers: [{
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => CheckboxGroupComponent),
      multi: true
    }]
 })
export class CheckboxGroupComponent implements ControlValueAccessor {
  private _model: any;

  // here goes implementation of ControlValueAccessor
  ...

  addOrRemove(value: any) {
    if (this.contains(value)) {
        this.remove(value);
    } else {
        this.add(value);
    }
  }

  contains(value: any): boolean {
    if (this._model instanceof Array) {
        return this._model.indexOf(value) > -1;
    } else if (!!this._model) {
        return this._model === value;
    }

    return false;
  }

  // implementation for add and remove
  ...
}

Checkbox component:

@Component({
selector: 'checkbox',
template: `
  <div (click)="toggleCheck()">
    <input type="checkbox" [checked]="isChecked()" />
    <ng-content></ng-content>
  </div>`
})
export class CheckboxComponent {
  @Input() value: any;

  constructor(@Host() private checkboxGroup: CheckboxGroupComponent) {
  }

  toggleCheck() {
    this.checkboxGroup.addOrRemove(this.value);
  }

  isChecked() {
    return this.checkboxGroup.contains(this.value);
  }
}

Child checkbox controls have reference to group component (@Host() decorator). When a checkbox is clicked toggleCheck() call addOrRemove() method on group component and if checkbox's value is already in the model, it is removed, otherwise it is added to the model.

8 Comments

Though it looks more complicated at a glance this is the best answer ive found on the topic, those classes are very reusable and clean to use once included, going in the toolbox, thank you.
There is one small issue with it, the labels of the check boxes do not trigger change events, only the actual checkbox portion. See this plunker plnkr.co/edit/BAhzLYo9e4H8PdAt9lGR?p=preview try clicking both the checkbox square and the label, both trigger the checkbox but only the checkbox portion fires a change.
@owlyfool I don't understand what you are referring to. toogleCheck() is called when you click on the div which contains a checkbox and a label. So clicking on checkbox and label fires the same behavior.
[x] <-- clicking here fires (change) which i can handle in the parent component (gets logged to the screen) [x] checkbox label <-- clicking the label directly does not (but it does update the modal as you say).
You need to put (change)="..." on checkbox-group component. It is the component that holds the model value.
|
2

For Angular 2+ check this example link

enter image description here

1 Comment

Link doesn't work anymore please update or remove the answer.
1

You can get array of checked data:

<input .... (change)="onChange(data)" />
onChange(data){
   data.checked = !data.checked;
}

to get all checked value

let checkedValues = userdata.filter(x => x.checked).map(x => x.email);

1 Comment

How does "data" relate to "userdata"? I'm also confused because you only show one input, but data does not seem keyed, so if I had 10 of these they would all just toggle one "data" variable, wouldn't they? Last, I'm not sure I understand the "x.email" reference. I'm trying to do exactly what your example intends, but don't know enough to put together the missing pieces. Any help would be appreciated!
0

I did this function at input for the load form auto select:

[checked]="this.selected? this.selected.type.includes(type) : false"

Full code:

<label *ngFor="let type of this.entityType" class="checkbox-inline c-checkbox">
<input type="checkbox" [checked]="this.selected? this.selected.type.includes(type) : false" (change)="onChangeEntityType(type, $event.target.checked)"/>
<span class="fa fa-check"></span>{{type | translate}}</label>

Where

this.selected

is my object and

this.selected.type.includes(type)

auto check if checkboxes will be checked.

Comments

0

I have solved in two different ways One with simple checkbox array - mat-checkbox and the other with mat-selection-list.

Gist of the code in the post. At the value you can set all kind of combinations. In my case I've used concatination of id and description in order to init the form controls at the end from one source..

https://medium.com/@2bhere4u/angular-5-material-design-checkbox-array-reactive-forms-handle-3885dde366ca

My issue was with cleaning the checkbox array in a more simple example.. No time to waste .. Running along to the next tasks.. :)

Comments

0

This was my approach, I used this for ionic cordova Contacts plugin to get multiple Contacts from addressbook and add the selected Contact.

HTML

<div *ngFor="let c of addressBook" tappable class="px-2">
  <input type="checkbox"  (change)="onChangeContact($event,c)">
  <p>{{ c.displayName }}</p>
 </div>

TS

onChangeContact($event, c) { 
    if ($event.target.checked) {
      this.tempArr.push(c);
    } else {
      let i: number = 0;
      this.tempArr.forEach((item: any) => {
        console.log(item);
        if (item.displayName == c.displayName) {
          this.tempArr.splice(i, 1);
          return;
        }
        i++;
      });
    }
  }

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.