1

I have this simple angular filter based on an injected value. I need to initialize the value asyncrhonously, receiving the data from a $http response. The point is that even if the value has been set correctly, the filter once has been evalueted does not fire a second time. I tried to put $scope.$apply() after angular.module("app").value("deferredValue", "foo"); but in this case I get an error because the $digest is already in progress. How can I fix my code to make it work?

angular.module("app", [])
.value("deferredValue", "")
.filter("myfilter", ["deferredValue", function(deferredValue){
  return function(input){
    if(deferredValue == input)
      return "1234";
    else
      return "4321";
  };
}])
.controller("ctrl", ["$scope","$http", function($scope, $http){
  $scope.test = "foo";
  
  $http.get("http://www.google.com").then(function(){
    angular.module("app").value("deferredValue", "foo");
  }).catch(function(){
    angular.module("app").value("deferredValue", "foo");
  });
}])
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>

<div ng-app="app" ng-controller="ctrl">
  {{test | myfilter}}
</div>
Luca
  • 153
  • 10
  • You can initialize data inside the filter. Please check out this question: [LINK](http://stackoverflow.com/questions/19046641/angularjs-asynchronously-initialize-filter) – Marcin Dusza Oct 11 '16 at 10:47
  • 1
    The filter does not get called unless the value of `test` changes. You can make it stateful, but then it will be called in **every** digest cycle. – a better oliver Oct 11 '16 at 10:50
  • When you set `deferredValue` to an empty string it is injected into your filter. Updating this service won't re-inject the service into the filter. If you debug through the code you can see that updating the value to `foo` does call the filter, but the value of `deferredValue` is still an empty string. You could always set `deferredValue` on the controller scope and pass this into your filter – Corporalis Oct 11 '16 at 11:09
  • @zeroflagL Your is the best answer. Please type below so that I can accept it. I tried to modify the filter and works perfectly. Thank you. – Luca Oct 11 '16 at 12:00
  • It is strongly discouraged to write filters that are stateful, by angular itself. Why don't you use model, that can be pass as parameter to filter. – ram1993 Oct 11 '16 at 12:22
  • @ram1993 Because the filter must be a reusable component applied to markups belonging to different controllers. Probably the usage of stateful filters is not a best practice but attaching the same collection to many controllers/scopes I think is not a best practice too. Probably, also put my value in the root scope is not a best practice. – Luca Oct 11 '16 at 13:02
  • @Luca what about performance? as mentioned in doc. – ram1993 Oct 11 '16 at 13:42
  • @ram1993 You are right, after some more test I saw it fires around 200 times for just 1 filter in my page. Not the best solution. Any other suggestion to make my filter reusable and performant as well? – Luca Oct 11 '16 at 13:55

2 Answers2

1

you should use statefull filter

filter("myfilter", ["deferredValue", function(deferredValue){
    function filter(input){
       if(deferredValue == input)
           return "1234";
       else
           return "4321";
    };
    filter.$stateful = true;

    return filter;
}])

https://docs.angularjs.org/guide/filter#stateful-filters

Valery Kozlov
  • 1,557
  • 2
  • 11
  • 19
1

use model, that can be pass as parameter to the filter.

app
.value("deferredObj", {value:''})
.filter("myfilter", [function(){
  return function(input, defered){
    if(defered == input)
      return "1234";
    else
      return "4321";
  };
}])
.controller("ctrl", ["$scope","http", 'deferredObj', function($scope, http, deferredObj){
  $scope.test = "foo";
  $scope.deferredObj = deferredObj;
  http.get("http://www.google.com").then(function(list){
    $scope.deferredObj.value = 'foo';
  }).catch(function(){
    $scope.deferredObj.value = 'foo';
  });
}]);

markup:

{{test | myfilter : deferredObj.value}}
ram1993
  • 979
  • 1
  • 9
  • 13