1

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:

  • if all values for this property are the same on all objects, display that value
  • if at least one value is not the same, set the input.value to 'multiple'
  • if all values are undefined, set the input.value to 'none'

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?

9
  • do you plan to select a property from a select then checkCondition and return a result? do you have one input or input per property? Commented Mar 26, 2014 at 11:49
  • If your application will run only on fairly modern browsers, maybe getters/setters will help you: Documentation Commented Mar 26, 2014 at 11:53
  • @wickY26 If you mean a <select>, then no, I am not using a select. I have one input per property. Commented Mar 26, 2014 at 11:53
  • @Nikos Paraskevopoulos Perhaps. i don't think that changes much. I still have to make a controller for each input in order to declare a new scope with the getter / setter on it, and even then the set will have to take place inside a $scope.$apply or angular won't know to propagate updates... Commented Mar 26, 2014 at 11:58
  • I am not sure I understand your concept correctly. Is this what you want? Commented Mar 26, 2014 at 12:11

1 Answer 1

2

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;
            }
        }
    };
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.