1

I would like to try and have a generic function in my controller that I can use from my view to tell a specific variable within the scope to change to a specific value.

Simplified example

Controller

$scope = {
    hair: {
        color: 'blue',
        length: '2cm'
    },
    mouth: 'wide',
    arms: [
        {
            fingers: 5,
            color: 'brown'
        },
        {
            fingers: 4,
            color: 'white'
        }
    ]
}

$scope.updateVariable = function(scopeVariable, value){
    scopeVariable = value;
}

View

<a ng-click="updateVariable(hair.color, 'red');">red hair</a>
<a ng-click="updateVariable(hair.length, '5cm');">increase hair length</a>
<a ng-click="updateVariable(mouth, 'narrow');">narrow mouth</a>
<a ng-click="updateVariable(arms[0].fingers, 4);">4 fingers</a>

It seems only the value of the variable is passed on to the function but not the reference. Is there a way for me to get the reference to the scope variable instead of its value from a function parameter? Furthermore can that be done dynamically? And by this I mean I need to pass the "path" to where this variable is located in the scope.

I am aware that this can be done with independent setter functions (i.e.: setMouth('narrow')) but let's assume for the sake of this exercise we do not know ahead of time the structure of the scope in the controller but only in the view and because of that we need a generic function that can deal with property.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
Gustavo Hoirisch
  • 1,637
  • 12
  • 19

2 Answers2

1

It seems only the value of the variable is passed on to the function but not the reference.

Correct.

Is there a way for me to get the reference to the scope variable instead of it's value from a function parameter?

No, JavaScript simply does not have that (which is called pass by reference). Instead, you can pass the name of the variable and then use that name in your function:

<a ng-click="updateVariable('hair.color', 'red');">red hair</a>
<a ng-click="updateVariable('hair.length', '5cm');">increase hair length</a>
<a ng-click="updateVariable('mouth', 'narrow');">narrow mouth</a>
<a ng-click="updateVariable('arms[0].fingers', 4);">4 fingers</a>

then apply the techniques in this question and its answers to update $scope from the path. Adapting the function from the answer by Alnitak to make it a setter:

Object.setByString = function(o, s, value) {
    s = s.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties
    s = s.replace(/^\./, '');           // strip a leading dot
    var a = s.split('.');
    for (var i = 0, n = a.length; i < n; ++i) {
        var k = a[i];
        if (k in o) {
            if (i == n - 1) {
                o[k] = value;
                return value;
            }
            o = o[k];
        } else {
            return value;
        }
    }
    return value;
};

then

$scope.updateVariable = function(scopeVariablePath, value){
    Object.setByString($scope, scopeVariablePath, value);
};
Community
  • 1
  • 1
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
1

If you pass objects around, they will be by reference.

For instance, rather than passing hair.color back, if you sent hair back instead it would work.

So to update as well?

Set it in the click is one option .

ngclick="hair.color = 'red'"

Also you can pass properties around.

Dot notation and bracket notation with variables are the same.

So hair.color is the same as hair["color"] - the later can be dynamic. You could pass a property name and update it .

Shaun
  • 933
  • 9
  • 16
  • The notion that objects are passed around as reference is good to know however your answer still assumes some knowledge of the scope structure in the controller. On the other hand assigning it in the`ng-click` handler makes me lose the ability to delay this assignment or defer it to another function which is really what I am trying to do. – Gustavo Hoirisch Nov 16 '15 at 22:42
  • You asked about object references in your question. End of story is pass an object. You were the one that bound the view with hard coded attributes. You also shouldn't assign to $scope like you are. You set on it, like $scope.blah you don't redefine $scope = {} - which is probably half your problem. – Shaun Nov 16 '15 at 22:58
  • No need to get agressive here buddy. You can clearly see it says "SIMPLIFIED EXAMPLE". I am pretty sure I got the idea across since the other answer that was not yours was spot on. – Gustavo Hoirisch Nov 16 '15 at 23:29
  • 1
    Not being aggressive at all, apologies if it came across that way. I only responded as the other answer is misleading IMO implying reference types aren't in Javascript. Doesn't change the fact you're assigning scope incorrectly, and passing strings around to represent underlying objects is an anti-pattern. – Shaun Nov 16 '15 at 23:35
  • I appreciate that and I definitely recognize the beauty of passing objects around like I mentioned in my first comment. I am not so sure about the anti pattern however as I can't change the structure coming from the backend so the fact that stand for me is to `Just get it to work`(^tm). – Gustavo Hoirisch Nov 16 '15 at 23:54