How to display same computed value multiple times in AngularJs view by calling compute function only once

277 Views Asked by At

In my view I want to display a computed property multiple times, if I do {{ctrl.Compute()}} multiple times, then compute function get called multiple times.

I have created this plunkr demo to simply my question http://plnkr.co/edit/TcMcJUipLKk94dthnivU

Controller

app.controller('MainController',function(){
  var ctrl= this;
  var list = [];

  ctrl.count = function(){
    console.log('Invoked');
    return list.length
  }


  ctrl.add = function(){
    list.push(1)
  }

});

View

 <body ng-controller="MainController as ctrl">
    <button ng-click="ctrl.add()">Add</button>
    <br>
    List Size: {{ctrl.count()}} <br>
    List Size: {{ctrl.count()}} <br>
    List Size: {{ctrl.count()}} <br>
    List Size: {{ctrl.count()}} <br>
  </body>

In view, you can see I am calling {{ctrl.count()}} four times and that means computation is happening four times. How can I do computation only once and display a value multiple times.?


Please don't suggest, ideas like, make array part of controller like ctrl.list, then in view use {{ctrl.list.length}}. This idea might for this eg but wont work where a complex computation is required.

4

There are 4 best solutions below

0
tdebroc On

Maybe something like this:

var savedCount = ctrl.count();
ctrl.count = function(){
    console.log('Invoked');
    return list.length
}

$scope.$watch("list", function() {savedCount = ctrl.count()});
4
Joy On

Check Plunker. Use a new variable on the scope like:

ctrl.counter = ctrl.count();
$scope.$watch(function () {
  return list;
}, function () {
  ctrl.counter = ctrl.count();
}, true);

On the HTML:

List Size: {{ctrl.counter}} <br>
List Size: {{ctrl.counter}} <br>
List Size: {{ctrl.counter}} <br>
List Size: {{ctrl.counter}} <br>

The $watch function watches every change of the value list, and call the callback function every time.

4
masimplo On

You can use a technique called memoization.

In computing, memoization is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls and returning the cached result when the same inputs occur again.

You can use a popular library like lodash or write it on your own (not encouraged).

You can find a usage example similar to your use case here talking about filters that are calculation heavy, similar to using binding to calculation functions.

2
Ramesh Rajendran On

You can do it by using bindHtmlUnsafe directive, and also Your count object is should be a $scope object, then it's possible. please just copy paste my answer.

    app.controller('MainController',function($scope){
     $scope.list = [];  

      $scope.context = '<br/> List Size: ' +
       $scope.list.length + ' <br/>List Size: ' + $scope.list.length + 
      '<br/>List Size: ' + $scope.list.length + ' <br/> List Size: ' + 
        $scope.list.length + ' <br/>';

     $scope.add = function () {
        $scope.list.push(1);
        $scope.context = '<br/> List Size: ' + $scope.list.length + ' <br/>List  
        Size: ' + $scope.list.length + '<br/>List Size: ' + $scope.list.length + 
       '<br/> List Size: ' + $scope.list.length + ' <br/>';
    }    
  });

app.directive('bindHtmlUnsafe', function ($compile) {
    return function ($scope, $element, $attrs) {

        var compile = function (newHTML) { // Create re-useable compile function
            newHTML = $compile(newHTML)($scope); // Compile html
            $element.html('').append(newHTML); // Clear and append it
        };

        var htmlName = $attrs.bindHtmlUnsafe; // Get the name of the variable 
        // Where the HTML is stored

        $scope.$watch(htmlName, function (newHTML) { // Watch for changes to 
            // the HTML
            if (!newHTML) return;
            compile(newHTML);   // Compile it
        });

    };
});

then

<body ng-controller="MainController">
      <button ng-click="add()">Add</button>
            <div bind-html-unsafe="context"></div>
  </body>

This is the one optional solution for your question :)

please see in Plunker how can i solve this issue with your angular version codes.