Reputation: 2643
Given an array of objects (such as would represent a selection of items, for example), and an input, how do you go about binding the input value so that it represents a given property of all the objects?
The input should display state in the manner:
I have the function that aggregates the values for a given property exposed on the scope:
// Return value (if all values are equal) or undefined (if not)
scope.getSelection('property')
I also have the function that sets a value on all the objects:
scope.setSelection('property', value)
I can't find a combination ng-value, ng-model and ng-change that allows me to both get from .getSelection() and set to .setSelection() automatically, so I'm assuming I have to write a new directive.
What's the idiomatic way to solve this problem?
Upvotes: 1
Views: 830
Reputation: 40298
For the sake of future reference, let me write a full answer:
A way to accomplish this in fairly modern browsers is using property getters/setters (spec). An example, proof-of-concept implementation would be:
Let's say the $scope
contains the following collection:
$scope.items = [
{id: 1, prop: "a"},
{id: 2, prop: "a"},
{id: 3, prop: "a"}
];
And we want to manipulate the aggregate of the item.prop
property. We define another object as:
$scope.form = {
get aggregate() {
var res, i;
for( i=0; i < $scope.items.length; i++ ) {
if( typeof(res) === "undefined" ) {
res = $scope.items[i].prop;
}
else if( $scope.items[i].prop !== res ) {
return "(multiple)";
}
}
return res;
},
set aggregate(val) {
var i;
for( i=0; i < $scope.items.length; i++ ) {
$scope.items[i].prop = val;
}
}
};
The form.aggregate
property now has a getter and setter. These function handle their values by iterating over $scope.items
. The getter compares the values and returns the common one, if all are the same or "(multiple)"
if at least one is different. The setter just sets the given value to all properties.
A fiddle: http://jsfiddle.net/52HE6/
And an improved (IMO) version, using a placeholder instead of the literal "(multiple)"
: http://jsfiddle.net/52HE6/1/
This pattern can probably be generalized/parameterized (i.e. no fixed name prop
), e.g. as (WARNING: UNTESTED):
function aggregatorFactory($scope, collectionName, propName) {
return {
get aggregate() {
var res, i;
for( i=0; i < $scope[collectionName].length; i++ ) {
if( typeof(res) === "undefined" ) {
res = $scope[collectionName][i][propName];
}
else if( $scope[collectionName][i][propName] !== res ) {
return "(multiple)";
}
}
return res;
},
set aggregate(val) {
var i;
for( i=0; i < $scope[collectionName].length; i++ ) {
$scope[collectionName][i][propName] = val;
}
}
};
}
Upvotes: 2