Reputation: 136
I have some data that I render in using angular's ng-repeat. I want to allow the user to select elements in view by checking checkboxes in front of each element. The simplest thing would be to add a 'selected' property to each element in the model. However I'd prefer to separate concerns more finely and not pollute the data model with the state of the UI. Also I'm sending the data back to the server and I'd prefer to not have to sanitise it before doing that. So the best thing IMO would be to have a separate selection model. Basically I'm trying to do something like this:
Controller:
$scope.data = [
{'name': 'a', 'value': '1'},
{'name': 'b', 'value': '2'},
{'name': 'c', 'value': '3'}
];
$scope.selection = {'a': false, 'b': false, 'c': false};
$scope.createSelectionGetterSetter = function(name) {
return function(newValue) {
if (angular.isDefined(newValue)) {
selection[name] = newValue;
}
return selection[name];
}
};
View:
<div ng-repeat='row in data'>
<input type='checkbox' ng-model='createSelectionGetterSetter(row.name)' ng-model-options='{ getterSetter: true }' />
<input type='text' ng-model='row.value' />
</div>
The idea here is to have createSelectionGetterSetter
create a closure that will map the selection state to the correct element in the selection model. Of course it doesn't work because Angular expects an actual getter/setter function in ng-model.
How do create such closures and pass them to the angular view as getters/setters?
Upvotes: 2
Views: 1136
Reputation: 40298
What you want can easily be accomplished using $watchCollection
to define the selection
variable:
$scope.$watchCollection('data', function(newval) {
$scope.selection = {};
if( newval ) {
angular.forEach(newval, function(val) {
$scope.selection[val.name] = false;
});
}
});
And using it as:
<input type='checkbox' ng-model='selection[row.name]' />
See fiddle: http://jsfiddle.net/54u0vztr/
The catch: Some tweaking is required if you want to specify initial value for the selection
or want it to retain its values when the data
changes. I believe these are more or less trivial to implement.
If you want to use the getterSetter
feature though, just add $scope.
selection[name]
to your code!
Fiddle: http://jsfiddle.net/qc5qtg5q/
The catches:
With this method the accessor function is constantly recreated. This may have impact on performance (both speed and memory usage), at least on a real page. To visualize it, add a console.log('Creating accessor')
at the beginning of the createSelectionGetterSetter
function.
You need extra handling for the case when the data
list changes, although you can now easily specify initial values for the selection
.
Upvotes: 1