3

I have this code:

<!DOCTYPE html>
<html ng-app="m1">
  <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular.min.js"></script>
  </head>
  <body>
    <div ng-controller='c'>
      <input ng-model='x1'><br>
      <span>{{x2}}</span>
    </div>
  </body>
  <script>
    var m = angular.module("m1", []);
    m.controller('c', function($scope){
      $scope.x2 = $scope.x1;
    });
  </script>
</html>

When I type into the input box, I don't see it getting reflected in the {{x2}} output.

But when I do it via a function, it works:

<!DOCTYPE html>
<html ng-app="m1">
  <head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular.min.js"></script>
  </head>
  <body>
    <div ng-controller='c'>
      <input ng-model='x1'><br>
      <span>{{x2()}}</span>
    </div>
  </body>
  <script>
    var m = angular.module("m1", []);
    m.controller('c', function($scope){
      $scope.x2 = function(){
        return $scope.x1;
      }
    });
  </script>
</html>

Why is this not working in this case? Shouldn't x2 get updated during the digest cycle?

1
  • 1
    whats about a watch on x1? something like $scope.$watch('x1', function(newVal, oldVal){ $scope.x2 = $scope.x1}); Commented Aug 12, 2016 at 13:08

3 Answers 3

5

1. Answer to your first question:

When I type into the input box, I don't see it getting reflected in the {{x2}} output

This is because x1 or x2 are plain literals and in Javascript, literals are not passed by reference but objects are. So if you would have created an object to reference the value, it might have worked. For example:

var m = angular.module("m1", []);
m.controller('c', function($scope) {
  $scope.x1 = {myModel: ''};
  
  $scope.x2 = $scope.x1;
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular.min.js"></script>

<div ng-app="m1" ng-controller='c'>
  <input ng-model="x1.myModel">
  <br>
  <span>{{x2.myModel}}</span>
</div>

The above will work because x1 is an object and when you assign it to x2, it's reference is passed hence both are same. This was not the case when you were using x1 as the plain literal string.

The above will again not work when you clone or copy the object using angular.copy. See an example of the same:

var m = angular.module("m1", []);
m.controller('c', function($scope) {
  $scope.x1 = {myModel: ''};
  
  $scope.x2 = angular.copy($scope.x1);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular.min.js"></script>

<div ng-app="m1" ng-controller='c'>
  <input ng-model="x1.myModel">
  <br>
  <span>{{x2.myModel}}</span>
</div>

This is because now you are not actually passing by reference but you are literally cloning the object. Now this is became same as your first problem. You are only copying the value from x1 to x2 at the time when your controller is initialized but not when your value in x1 is modified.

2. Answer to your second question:

But when I do it via a function, it works

When you used function, it worked because it is a feature of Angular that when you write a scope function in Angular and you use it in your view like {{x2()}} then Angular will auto update the view whenever the returned value from the function (x2() in this case) is changed/updated.

3. Solutions

There are multiple solutions to this problem that others have already mentioned.

3.1 Use $watch

(https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$watch)

var m = angular.module("m1", []);
m.controller("c", function($scope) {
  $scope.$watch("x1", function(newValue) {
    $scope.x2 = $scope.x1;
  });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular.min.js"></script>

<div ng-app="m1" ng-controller='c'>
  <input ng-model="x1">
  <br>
  <span>{{x2}}</span>
</div>

3.2 Use ng-change

(https://docs.angularjs.org/api/ng/directive/ngChange)

var m = angular.module("m1", []);
m.controller("c", function($scope) {
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.7/angular.min.js"></script>

<div ng-app="m1" ng-controller='c'>
  <input ng-model="x1" ng-change="x2 = x1">
  <br>
  <span>{{x2}}</span>
</div>

In this way, you don't need any Javascript code.

3.3 Use objects

Use object instead of plain String literals like I mentioned above to avoid this problem.

3.4 Use a function

Use the function feature of Angular like you already mentioned in your question.

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

Comments

1

You can also use a watcher:

var m = angular.module("m1", []);
m.controller('c', function($scope){        
  $scope.$watch(function() { return $scope.x1; },
      function(newValue, oldValue) {
        $scope.x2 = $scope.x1
      }
    );
});

this way, x2 will be updated at the same time of x1.

Comments

0

Because once your controller is defined, x1 is assigned to x2 only once. In order to see x2 updated, you need to add ng-change directive to input, that will fire a function that assigns x1 to x2:

<body>
    <div ng-controller='c'>
        <input ng-change="changed()" ng-model='x1'><br>
        <span>{{x2}}</span>
    </div>
</body>
<script>
    var m = angular.module("m1", []);
    m.controller('c', function($scope){
        $scope.changed = function() {
            $scope.x2 = $scope.x1
        }
    });
</script>

Example: https://plnkr.co/edit/yl6eiLrDeIhMiQtJxPlz?p=preview

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.