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
source to share
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
source to share
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);
}
source to share
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.
source to share