1

I have an input in my view that is bound to a scope property as follows:

<input ng-model="searchTerm">

When text is entered, it can be used to filter a repeater:

<ul>
   <li ng-repeat="item in Array | filter: searchTerm"></li>
</ul>

This is fine but I have a problem when I want to filter the array in the controller and repeat on this filtered array instead:

$scope.filteredArray = $filter('filter')($scope.array, $scope.searchTerm);

<ul>
  <li ng-repeat="item in filteredArray"></li>
</ul>

When I enter text into my input the searchTerm property does not update the filtered array. I have been able to get it to work by adding an ng-keyup directive to my input to watch for text entered and to update the filtered array accordingly:

In view:

<input ng-keyup="updateFilteredArray()" ng-model="searchTerm">

<ul>
  <li ng-repeat="item in filteredArray"></li>
</ul>

In controller:

$scope.filteredArray = $filter('filter')($scope.array, $scope.searchTerm);

$scope.updateFilteredArray = function() {
   $scope.filteredArray = $filter('filter')($scope.array, $scope.searchTerm);
}

This works perfectly but it seems strange to me that I need to define both the filteredArray property and the updateFilteredArray method in my controller. Can anyone suggest if this is the way I should be doing it or if there is a way to update the filter without ng-keyup directive? I thought that 2 way data binding would have negated the need for a specific method to update the filtered array, and that when text is entered the property would update wherever it is used in the view and the controller?

1
  • If i understand you question, the code in your controller runs once. So when it runs the filter is applied. Values of your scope which are data bound so searchTerm will be updated. However the filter has already run by that point and will not run again, data-binding doesn't work like that. Applying the filter in your view means data binding is applied and thats why it works. Adding the function handler also works because data-binding means it will call updateFilteredArray on every key up event which will in turn filter your array in your controller, data-binding on the array does the rest Commented Feb 25, 2016 at 15:32

2 Answers 2

1

Try this:

<ul>
   <li ng-repeat="item in filteredArray = (array | filter: searchTerm)"></li>
</ul>

If you do this, $scope.filteredArray will always be updated in the controller - no need for a method.

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

4 Comments

Thanks, this does produce the same result that I had achieved without the ng-keyup. However, if I want to then set up another property in my controller based on the filteredArray eg $scope.filteredArrayCount = $scope.filteredArray.length; and use this property in my view, the count will not update when the length of the filtered array changes. This could be a separate question but it seemed like this would work if the filteredArray property was updated successfully. Any ideas?
If you use this in your view it will be updated as the array is filtered: {{ filteredArray.length }} - there is no need to use this in the controller at all. If you need to do some logic with the length inside of the controller, it will only reflect the value at the time the controller code is run; the logic would have to be executed again every time the length changes. If you only want to hide/show view elements based on different lengths, I recommend using ng-show/ng-hide like this: <span ng-show="filteredArray.length === 0">No results</span>
You could also put an ng-change on the input field to run code when the search text changes: <input type="text" ng-model="searchTerm" ng-change="someMethod()"> Where someMethod is a method in your controller.
You've pretty much solved my problem. I guess I need to include as much logic in the view as possible. My goal is to paginate the results, with the count of the results where I have a "showing 1 -10 of 43" and buttons that show the previous/next 10 of the filtered results when clicked. This requires me to get the count of the filtered array ( for the count) and to slice the filtered array whenever it changes (to show a chunk of the results from the appropriate index). I've now got the page count working thanks to your suggestion. Now I'll try get the next/previous 10 results buttons to work
0

You could use simply a $watch to observe the searchTerm without using the ng-keyup directive:

Controller:

$scope.$watch('searchTerm', function(newValue, oldValue) {
  $scope.filteredArray = $filter('filter')($scope.array, newValue);
});

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.