Reputation: 3144
I have a row of checkboxes, only one can be checked at a time, and if you click on the checkbox that is checked, its state should flip. So if it is checked and you click it, it should uncheck, 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 isn't updated
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. model updates correctly in this one but you are unable to untoggle a checkbox by clicking on it again (hence the !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})
})
Need a solution without using radios
Upvotes: 3
Views: 249
Reputation: 12025
All you have to do is to change:
this.setDefault = (j) => this.models = this.models.map((m, i) => {
return j === i
? Object.assign({}, m, {defaulted: true})
: Object.assign({}, m, {defaulted: false})
})
To
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 allow to toggle the state of the current selected/unselected checkbox.
Edit: Please note that @PankajParkar's answer is better, because unlike my answer, that simply solve the main issue, that answer is also better for performances as it doesn't overwrite the this.models
array using Array.prototype.map()
- Which makes angular re-render the view in the ngRepeat
loop.
You're welcome to accept their answer instead
Upvotes: 2
Reputation: 7428
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;
}
});
};
Collected some downvotes, but it actually works pretty well.
https://plnkr.co/edit/BhE7XF?p=preview
Upvotes: 0
Reputation: 136194
I don't like the way you played with index
of an array element. It will not work if you apply any filtering over models
collection. Rather I'd suggest you to pass current selected model to setDefault
function like below and make current model flag true
, other will be set to false
.
Also there is no need to use Object.assign
to everytime create a new copy of an object. Rather looping and modifying the 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);
}
Upvotes: 2
Reputation: 3144
I said screw it 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
Upvotes: 0