3

Question

Is it possible to pass a scope variable (specifically an array) by reference to a function called using ng-click, and manipulate the value of said variable?

Update: To be more explicit, my goal here is to avoid having to access $scope in $scope.app.onItemClick.

Example

Javascript:

(function() {
    angular.module('test', [])
    
    angular.module('test').controller('test',['$scope', function ($scope) {
      $scope.app = {};
      
      $scope.app.primarySet = [
        'test1',
        'test2',
        'test3'
      ]
      
      $scope.app.workingSet = [
        'test4',
        'test5',
        'test6'
      ]
      
      /*
       * Attempts to assign $scope.app.primarySet to $scope.app.workingSet using references to those two values.
       */
      $scope.app.onItemClick = function (primarySet, workingSet) {
        primarySet = workingSet
      }
    }])
  })()

Relevant HTML:

<button type="button" ng-click="app.onItemClick(app.primarySet, app.workingSet)">Update Primary Set</button>

Please see this Plunker for this code in more context.

My expectation for this code is that $scope.app.primarySet would be set to $scope.app.workingSet when the button is pressed. Unfortunately, this is not the case. While debugging, in the scope of the function, primarySet is assigned to workingSet. However $scope.app.primarySet is not.

Motivation

My motivation for this is rooted in this SO reply. I share the belief that it will be easier to test methods that manipulate scope if I am not referencing it directly. I also find this more straightforward than having a function manipulate the scope directly.

Previous Research

The only resource that I have come across relating to this is this SO question. While this question comes close, it is different in that the parameter in question is a string, which, if I understand correctly, cannot be modified as a reference could be expected to.

5
  • did you tried: <button type="button" ng-click="app.primarySet = app.workingSet">Update Primary Set</button> Commented Jan 20, 2016 at 17:54
  • @oxigenao, yes, that does work, but I don't find it to be a good long term solution as that mapping shouldn't be in the template and isn't testable. Commented Jan 20, 2016 at 17:59
  • oxigenao is correct in their suggestion, when dealing with primatives as an inheritance it is suggested as a best practice to use '.' The prototypical inheritance you are working with is a result of javascript and not angular. github.com/angular/angular.js/wiki/Understanding-Scopes offers some good information on scope and how you can navigate through them Commented Jan 20, 2016 at 18:09
  • goal to not use $scope.app still doesn't explain what you are trying to do with this function at all. Why are you not wanting to modify scope for testing in the first place? Commented Jan 20, 2016 at 18:14
  • @charlietfl, this function is the best abstraction I could come up with to a much larger concern in a much larger app. My motivation for not wanting to directly modify scope for testing is an attempt to be able to test just the function without any side effects and without having to mock scope. That and in my larger project that this represents, I have so many references to scope that it is getting downright confusing. While this may be a sign of poor code structure on my part, I am trying to find a pattern that enforces it. I hope that helps clear it up a bit. Commented Jan 20, 2016 at 18:23

2 Answers 2

1

You can change the primarySet in $scope if you pass the $scope.app trough onItemClick, like this:

  (function() {
    angular.module('test', [])

    angular.module('test').controller('test',['$scope', function ($scope) {
      $scope.app = {};

      $scope.app.primarySet = [
        'test1',
        'test2',
        'test3'
      ]

      $scope.app.workingSet = [
        'test4',
        'test5',
        'test6'
      ]

      /*
       * Attempts to assign $scope.app.primarySet to $scope.app.workingSet using references to those two values.
       */
      $scope.app.onItemClick = function (app, workingSet) {
        app.primarySet = workingSet;
      }
    }])
  })()

<button type="button" ng-click="app.onItemClick(app, app.workingSet)">Update Primary Set</button>

And the reason is when you passed the array only the value was passed to the function.

Sign up to request clarification or add additional context in comments.

Comments

1

JavaScript allows you to access properties of objects as keys, so you would modify your template as follows:

<button type="button" ng-click="app.onItemClick(app, 'primarySet', app, 'workingSet')">Update Primary Set</button>

And modify the onItemClick method to:

  $scope.app.onItemClick = function ( leftObject, leftProperty, rightObject, rightProperty) {
    leftObject[leftProperty] = rightObject[rightProperty];
  }

2 Comments

While this works, it really is a nightmare what comes to maintenance and understanding what is happening :)
In most cases I would totally agree! This adds a lot of flexibility which isn't required in the toy example. I am however expecting the OP has a good set of supporting reasons for requiring this flexibility and will factor out unneeded complexity.

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.