kresho
kresho

Reputation: 136

How do I create closures for model getter-setter in angular?

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

Answers (1)

Nikos Paraskevopoulos
Nikos Paraskevopoulos

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:

  1. 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.

  2. 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

Related Questions