30

I've written the following proof-of-concept of an Angular app that allows a person to vote for President of the US.

<!DOCTYPE html>
<html ng-app="ElectionApp"">
<head>
    <title></title>
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.4/angular.min.js"></script>
    <script>
        var ElectionApp = angular.module("ElectionApp", []);
        var ElectionController = ElectionApp.controller("ElectionController", [function () {
            // Pretend that this data came from an outside data-source.
            this.candidates = {
                "14837ac3-5c45-4e07-8f12-5771f417ca4c": {
                    name: "Alice",
                    gender: "female"
                },"333eb217-c8d1-4b94-91a4-22a3770bbb22": {
                    name: "Bob",
                    gender: "male"
                }
            };
            this.vote = function () {
                // TODO: Record the user's vote.
            };
        }]);
    </script>
</head>
<body ng-controller="ElectionController as ctrl">
    Who would you like to elect President? <br />
    <select
        ng-model="ctrl.selection"
        ng-options="person as person.name for (id, person) in ctrl.candidates | filter{gender:'female'}">
    </select>
    <input type="button" value="Vote!" ng-submit="ctrl.vote();" />

    <h2>Candidate Profiles</h2>    
    <div ng-repeat="candidate in ctrl.candidates">
        {{candidate.name}}, {{candidate.gender}}
    </div>
</body>
</html>

My voting app displays a list of the candidates names along with a profile for each candidate, which in this case, consists of the candidate's name and gender.

For the purpose of this question, please pretend that the roster of candidates to choose from is fetched from a remote data source, but will follow the same format as the example.

Suppose that shortly before the election is to be held, a constitutional amendment is passed mandating that the next US President must be female. Suppose that the data source cannot be updated in time for the election, and the boss said to me, "I've heard that with Angular, you can arbitrarily choose which items from the data source appear on the form using a filter. Make it happen!"

Following along with some examples I've seen on-line, I've written the above code, but it no longer displays any candidate. What did I do wrong?

How can I filter the options in a select list using an Angular filter?

Vivian River
  • 31,198
  • 62
  • 198
  • 313
  • 2
    missing a : after filter – Vince Oct 22 '15 at 21:20
  • After doing more research, it looks like the same question was asked 2 years ago: http://stackoverflow.com/questions/14788652. The answer there is that Angular does not do this, but I would think that there must be some reasonable way. – Vivian River Oct 22 '15 at 21:40
  • "_I've heard that with Angular, you can arbitrarily choose which items from the data source appear on the form using a filter. Make it happen!_" Well if you use `ng-repeat` instead of `ng-options` to render your options that's definitely true. – ryanyuyu Oct 22 '15 at 21:49

3 Answers3

32

You just forgot ":" after filter keyword.

<select 
    ng-model="ctrl.selection"
    ng-options="person as person.name for person in ctrl.candidates | filter:{gender:'female'}">
</select>
  • The error that I got when first trying your solution above says "Not array". When I replace my dictionary with an array, it seems to work, but I would have thought that this sort of filtering would be basic functionality that should work with dictionary-style objects, as well. – Vivian River Oct 22 '15 at 21:35
  • The ng-optons and ng-repeat works fine with dictionary-style object (using the syntax (key, value) in myObj). But the filter seems to be a problem for that kind of data. If you don't want to use an array, you can still pre-filter your candidate dictionary in the controller. – Benoit Charpié-Pruvost Oct 22 '15 at 21:48
  • Unfortunately, in my real production application, it would need to be able to dynamically change the filter criteria, so I might just have to switch to using an array. – Vivian River Oct 22 '15 at 22:25
  • I switched to using an array and this solution works. – Vivian River Oct 23 '15 at 15:13
19

a bit late maybe, but for others looking for info I'm using this.

$scope.deliveryAddresses = data; 

Where data is complex Json id, name and city received from webapi in my Angular controller by $http.get

I'm using it in my Html page with the following snippet

ng-options="address.name for address in deliveryAddresses | filter:{name:search} track by address.id

where search is a reference to a textbox. Simple and efficient.

Hope it helps someone.

Han
  • 191
  • 1
  • 2
  • Oh man.. I was looking for this answer for like 3 days :D But I was asking the incorrect question :) This really simplifies my Angular now.. clearly I am a noob but at least I can refactor my code and this makes it sooo much easier and cleaner! Thanks! :D – Piotr Kula Mar 16 '17 at 14:29
  • 1
    `''` matches any primitive but null or undefined, so to filter for non-null values: `filter:{name: ''}` – JellicleCat Mar 03 '20 at 18:35
17

filter can only operate on arrays.

But you can create a custom filter:

ElectionApp.filter('females', [ function() {
    return function (object) {
        var array = [];
        angular.forEach(object, function (person) {
            if (person.gender == 'female')
                array.push(person);
        });
        return array;
    };
}]);

and then write

ng-options="name as person.name for (id, person) in ctrl.candidates | females">
wero
  • 32,544
  • 3
  • 59
  • 84
  • Judging from this and other answers, I have chosen to rework my code so that the select boxes use arrays for the ng-options. – Vivian River Oct 23 '15 at 15:14