2 ways data binding does not work

127 Views Asked by At

I am writing a simple controller which does few calculations. This is a reflection of a real project with more advanced information. The problem is that the result, when placed on the html as an expression is being recalcualted with every change, however, when I am calculating in the $scope as a variable, it does not update. see comment in the the markup.

Any idea what I am missing here?

markup

<body ng-app="myApp"> 
<div ng-controller="mainController">
  <h3> Numbers </h3> 
  <label> a </label>
  <input type="number" ng-model="numbers.a"/>

  <label> b </label> 
  <input type="number" ng-model="numbers.b">
<br>
<br>
  <span> Result : {{result}} {{numbers.a*numbers.b}} </span> // the first one does not update, but the second does. 

  <h3> Nums </h3> 

    <label> a </label>
  <input type="number" ng-model="a1">

  <label> b</label> 
  <input type="number" ng-model="b1">

<br>  
  Result2:  {{result2}} {{a1+b1}}
<ul> 
  <li ng-repeat=" i in cool"> {{i}} </li>
  </ul>
  </div>
</body> 

javascript:

angular.module('myApp',[])

.controller('mainController', ['$scope', function($scope) {
  $scope.numbers = {a:11, b:10}; 
  $scope.a1 = 5; 
  $scope.b1 = 7; 
  $scope.result = $scope.numbers.a*$scope.numbers.b; 
  $scope.result2 = $scope.a1 +$scope.b1 ;

  $scope.cool = [$scope.result + $scope.result2, 
               $scope.result - $scope.result2]
}]);

http://codepen.io/Tallyb/pen/rVdebm

3

There are 3 best solutions below

0
Remigius Stalder On

The calculations in the controller are only executed when the controller is instantiated, typically when the corresponding view is displayed. You'll have to encapsulate the calculation in functions (no $watch necessary):

$scope.result = function() {
  return $scope.numbers.a * $scope.numbers.b;
}
$scope.result2 = function() {
  return $scope.a1 + $scope.b1;
}
$scope.cool = function() {
  return [
    $scope.result() + $scope.result2(),
    $scope.result() - $scope.result2()
  ];
}

and reference it in your view:

<span> Result : {{result()}} {{numbers.a*numbers.b}} </span>

and:

Result2:  {{result2()}} {{a1+b1}}

and:

<li ng-repeat=" i in cool(result, result2)"> {{i}} </li>

see: http://codepen.io/anon/pen/vORXpg

0
Arun P Johny On

You need a watch to recalculate the value of result when the numbers is changed. Otherwise the value of result is calculated only once

var app = angular.module('my-app', [], function() {})

app.controller('mainController', ['$scope',
  function($scope) {
    $scope.numbers = {
      a: 11,
      b: 10
    };
    $scope.a1 = 5;
    $scope.b1 = 7;

    //if a or b is changed recalculate result
    $scope.$watch('[numbers.a, numbers.b]', function() {
        $scope.result = $scope.numbers.a * $scope.numbers.b;
      })
      //if a1 or b1 is changed recalculate result2
    $scope.$watch('[a1, b1]', function() {
      $scope.result2 = $scope.a1 + $scope.b1;
    })

    //if result or result2 is changed recalculate cool
    $scope.$watch('[result, result2]', function() {
      $scope.cool = [$scope.result + $scope.result2,
        $scope.result - $scope.result2
      ]
    })
  }
]);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="my-app">
  <div ng-controller="mainController">
    <h3> Numbers </h3> 
    <label>a</label>
    <input type="number" ng-model="numbers.a" />

    <label>b</label>
    <input type="number" ng-model="numbers.b" />
    <br/>
    <br/>
    <span> Result : {{result}} {{numbers.a*numbers.b}} </span> // the first one does not update, but the second does.

    <h3> Nums </h3> 

    <label>a</label>
    <input type="number" ng-model="a1" />

    <label>b</label>
    <input type="number" ng-model="b1" />

    <br/>Result2: {{result2}} {{a1+b1}}
    <ul>
      <li ng-repeat=" i in cool">{{i}}</li>
    </ul>
  </div>
</div>

0
riptidebyte On

The first variable result doesn't update, because your mainController function only gets evaluated once - which is the first time angular interprets the html and first discovers the expression ng-controller="mainController".

In order to get result to update automatically, you have to set up watch listeners in the controller like this:

angular.module('myApp',[])

.controller('mainController', ['$scope', function($scope) {
  // ...
  $scope.$watch('[numbers.a, numbers.b]', function () {
    $scope.result = $scope.numbers.a*$scope.numbers.b;
  });

}]);

An expression like {{numbers.a*numbers.b}} will update automatically, because angular will set up watch listeners for those. In fact, the expression syntax in the html is just syntactic sugar - under the hood, angular just invokes the same $watch functions for every expression it finds in the html.

See the $watch documentation for more detail.

I personally prefer not to use the $watch syntax mentioned above, because it bloats the controller. Alternatively, you can call a function from your html:

{{ calculateResult() }}

In your controller, you would then define the function like this:

angular.module('myApp',[])

.controller('mainController', ['$scope', function($scope) {
  // ...
  $scope.calculateResult = function () {
    return $scope.numbers.a*$scope.numbers.b;
  };

}]);

Side note: If you're concerned about performance and your calculateResult() function is really slow, you might want to stick to the first version though.