Как отображать одно и то же вычисленное значение несколько раз в представлении AngularJs, вызывая функцию вычисления только один раз

На мой взгляд, я хочу отображать вычисляемое свойство несколько раз, если я делаю {{ctrl.Compute()}} несколько раз, то функция вычисления вызывается несколько раз.

Я создал эту демонстрацию plunkr просто для того, чтобы задать свой вопрос http://plnkr.co/edit/TcMcJUipLKk94dthnivU

Контроллер

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

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


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

});

Просмотреть

 <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>

В представлении вы можете видеть, что я вызываю {{ctrl.count()}} четыре раза, а это означает, что вычисления происходят четыре раза. Как я могу выполнить вычисление только один раз и отобразить значение несколько раз?


Пожалуйста, не предлагайте такие идеи, как сделать массив частью контроллера, например ctrl.list, а затем в представлении использовать {{ctrl.list.length}}. Эта идея может быть для этого, например, но не будет работать там, где требуется сложное вычисление.


person Praveen Prasad    schedule 21.09.2015    source источник
comment
установите счетчик как переменную в контроллере $scope.arraylength = ctrl.count() и отобразите его как обычно. Затем вы можете вызвать функцию count() с помощью наблюдателя или определенных действий, в зависимости от вашего варианта использования...   -  person Karl Adler    schedule 21.09.2015
comment
computation is happening four times Нет, я думаю, что это просто вызывает функцию 4 раза. Я думаю, что js не буду его вычислять. это свойство объекта массива, которое прототипически наследуется от Array. поправьте меня, если я ошибаюсь :). developer.mozilla.org/en-US/ документы/Интернет/JavaScript/Справочник/   -  person Kalhan.Toress    schedule 21.09.2015
comment
Как я сказал для этой демонстрации, count () просто возвращает длину массива, но в реальном приложении я хочу выполнить сложные вычисления в этой функции. Если из представлений я вызываю функцию несколько раз, вычисления будут происходить несколько раз.   -  person Praveen Prasad    schedule 21.09.2015
comment
@abimelex: я не хочу использовать его таким образом, но я попробовал ваш код, но он не работает   -  person Praveen Prasad    schedule 21.09.2015
comment
@PraveenPrasad. вы нашли какие-нибудь решения?   -  person Ramesh Rajendran    schedule 21.09.2015
comment
@ramesh я мог бы использовать технику запоминания   -  person Praveen Prasad    schedule 21.09.2015


Ответы (4)


Может быть, что-то вроде этого:

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

$scope.$watch("list", function() {savedCount = ctrl.count()});
person tdebroc    schedule 21.09.2015

Вы можете использовать технику, называемую мемоизацией.

В вычислительной технике мемоизация — это метод оптимизации, используемый в основном для ускорения работы компьютерных программ путем сохранения результатов дорогостоящих вызовов функций и возврата кэшированного результата при повторении тех же входных данных.

Вы можете использовать популярную библиотеку, например lodash, или написать ее самостоятельно (не рекомендуется).

Пример использования, аналогичный вашему, можно найти здесь, где речь идет о фильтрах, которые являются тяжелыми для вычислений, похожими на использование привязки к функциям вычисления.

person masimplo    schedule 21.09.2015
comment
да, я также думал об использовании мемоизации, но я уверен, что для этого должно быть какое-то простое решение. - person Praveen Prasad; 21.09.2015
comment
Я использовал его в довольно многих случаях и обнаружил, что в отличие от игры с наблюдателями, вы должны быть очень осторожны с их работой, когда их увольняют, если они собираются вызвать цепную реакцию и т. д. - person masimplo; 21.09.2015
comment
У меня есть интуиция, что $watchers не эффективны, может быть, потому что я где-то это читал. Вот почему я планировал использовать запоминание. Вы также имеете в виду, что $watcher неэффективны? - person Praveen Prasad; 21.09.2015
comment
Это длинная тема, но если коротко, ВСЕ активные наблюдатели вызываются в каждом цикле дайджеста, поэтому, если у вас есть тяжелые вычисления даже в одном из них, это может снизить производительность всего приложения. Наблюдатели должны использоваться, когда это необходимо, но следует проявлять большую осторожность, поскольку не всегда очевидно, как часто они вызываются или какой ущерб они принесут в цикле дайджеста. Плохое использование наблюдателей — одна из причин, по которой Angular2 переключился на наблюдаемые объекты вместо грязной проверки. - person masimplo; 21.09.2015

Проверьте Plunker. Используйте новую переменную в области видимости, например:

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

В HTML:

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

Функция $watch отслеживает каждое изменение значение list и каждый раз вызывать функцию обратного вызова.

person Joy    schedule 21.09.2015
comment
$scope.$watch означает какой объект? - person Ramesh Rajendran; 21.09.2015
comment
можешь объяснить свое решение. Насколько эффективен $watch? - person Praveen Prasad; 21.09.2015
comment
Вы можете использовать watchCollection для повышения производительности, поскольку вам, вероятно, не нужны глубокие часы. - person masimplo; 21.09.2015
comment
Это немного выходит за рамки этого вопроса. См. документацию $watch: docs.angularjs.org/api/ng. /type/$rootScope.Scope - person Joy; 21.09.2015

Вы можете сделать это с помощью директивы bindHtmlUnsafe, а также ваш объект count должен быть объектом $scope, тогда это возможно. пожалуйста, просто скопируйте и вставьте мой ответ.

    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
        });

    };
});

потом

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

Это одно из возможных решений вашего вопроса :)

пожалуйста, посмотрите в Plunker, как я могу решить эту проблему с вашими кодами версии angular.

person Ramesh Rajendran    schedule 21.09.2015
comment
имейте в виду, что html-unsafe называется так, потому что это небезопасно. - person Karl Adler; 21.09.2015
comment
Вы пытаетесь решить эту конкретную проблему, которая меня не касается. Я хочу изучить концепцию, от которой ваше решение далеко. Я все еще ценю ваши усилия. - person Praveen Prasad; 21.09.2015