Can't set ng model on checkbox inside ng-repeat

I have a row of checkboxes, only one can be checked at a time, and if you click on the checked checkbox, its state should flip. So if it's checked and you click it, it should uncheck the box and vice versa.

model

this.models = [
  {name: 'blah', defaulted: false},
  {name: 'fdsf', defaulted: false},
  ...
]

      

template

<input 
  type="checkbox"
  ng-repeat="model in vm.models"
  ng-checked="model.defaulted === true" 
  ng-change="vm.setDefault($index)"
  ng-model="model.defaulted" />

      

a. this one doesn't work, model doesn't update

http://embed.plnkr.co/O275a9O7y60NuimFYdYi?show=preview

this.setDefault = (j) => this.models = this.models.map((m, i) => {
  return j === i 
    ? Object.assign({}, m, {defaulted: !m.defaulted}) 
    : Object.assign({}, m, {defaulted: false}) 
}) 

      

b. updates the model correctly in this, but you cannot undo it by clicking it again (hence !m.defaulted

in example a)

http://embed.plnkr.co/MOBuN06R0hmcnpGu01Z7?show=preview

// this works, but now you can't uncheck a box that is checked
this.setDefault = (j) => this.models = this.models.map((m, i) => {
  return j === i 
    ? Object.assign({}, m, {defaulted: true}) 
    : Object.assign({}, m, {defaulted: false}) 
}) 

      

Requires no radio solution

+3


source to share


4 answers


All you have to do is change:

this.setDefault = (j) => this.models = this.models.map((m, i) => {
  return j === i 
    ? Object.assign({}, m, {defaulted: true}) 
    : Object.assign({}, m, {defaulted: false}) 
}) 

      

For

this.setDefault = (j) => this.models = this.models.map((m, i) => {
  return j === i 
    ? Object.assign({}, m, {defaulted: m.defaulted}) 
    : Object.assign({}, m, {defaulted: false}) 
}) 

      



By changing it to Object.assign({}, m, {defaulted: m.defaulted})

, you can toggle the state of the currently selected / unselected checkbox.

Edit . Note that @PankajParkar's answer is better because, unlike my answer, this just solves the main problem, this answer is also better for performances since it doesn't overwrite the this.models

array with Array.prototype.map()

- which makes angular re-render the view in a loop ngRepeat

.

Instead, you can accept their answer

+2


source


I don't like the way you played with the index

array element. It won't work if you filter through the collection models

. Rather, I suggest you pass the currently selected model to the function setDefault

as shown below, and make the current model flag true

, the other will be set to false

. Also there is no need to use Object.assign

to create a new copy of the object. Rather, looping and modifying an existing array element makes more sense.

<input type="checkbox" 
  ng-change="vm.setDefault(model)"
  ng-model="model.defaulted" />

      

code



this.setDefault = (j) => {
    this.models.forEach(i => i.defaulted = i.defaulted && j.name === i.name);
} 

      

Demo plunker

+2


source


Html

<input name="foo" ng-change="vm.setDefault(opt)" ng-model="opt.defaulted" type="checkbox" ng-repeat="opt in vm.models" />

      

JavaScript

this.setDefault = function(opt) {
    this.models.forEach((val) => {
        if(opt != val) {
            val.defaulted = false;
        }
    });
};

      

Collect a few downvotes, but it actually works really well.

https://plnkr.co/edit/BhE7XF?p=preview

0


source


I said I screwed it up and used icons instead of checkboxes

<i
  ng-click="vm.setDefault($index)"
  ng-class="{'fa-circle-thin': model.defaulted === false, 'fa-circle': model.defaulted === true}"
  class="fa">
</i>

      

https://plnkr.co/edit/5dj865ew1dqUMM9cchrY?p=preview

0


source







All Articles