Angular ui-select filtering only one field

25,793

Solution 1

Maybe it's because the same value($select.search) is used for both filters email and name.

<ui-select-choices repeat="person2 in list_people | filter: {name: $select.search, email: $select.search, db_data_type_id: 5}">
...

This will also explains, why it only works with the first character.

Use separate values for each filter to fix this:

<ui-select-choices repeat="person2 in list_people | filter: {name: $select.search.name, email: $select.search.email, db_data_type_id: 5}">
...

Solution 2

You can use property filter like below

 <ui-select   ng-model="emplyee" theme="bootstrap">
 <ui-select-match placeholder="Select ...">{{$select.selected.firstName+" "+$select.selected.lastName}}</ui-select-match>
 <ui-select-choices repeat="emp in employees | propertyFilter: {firstName:$select.search, lastName:$select.search}">
 <span ng-bind-html="emp.firstName+' '+emp.lastName | highlight: $select.search"></span>
 </ui-select-choices>

You will have to modify the propertyFilter like below:

.filter('propertyFilter', function($log) {
 return function(items, props) {
    var out = [];
    if (angular.isArray(items)) {
    items.forEach(function(item) {
        var itemMatches = false;
        var keys = Object.keys(props);
        var optionValue = '';
        for (var i = 0; i < keys.length; i++) {
             optionValue = item[keys[i]] ? optionValue + item[keys[i]].toString().toLowerCase().replace(/ /g, '') : '';
        }
        for (var j = 0; j < keys.length; j++) {
            var text = props[keys[j]].toLowerCase().replace(/ /g, '');
            if (optionValue.indexOf(text) !== -1) {
               itemMatches = true;
               break;
            }
        }
        if (itemMatches) {
            out.push(item);
        }
        });
        } else {
            // Let the output be the input untouched
            out = items;
        }

        return out;
    };
})

Search can be done on the options value as a whole. For example if in options you have 'peter parker' then you can search with 'peter', 'parker','peter parker', 'peterparker' and even search with multiple spaces in between any character of peter parker.

Solution 3

I've seen a few floating around like the answer from kunsingh, which got me started to solve my similar problem. In my case it was searching multiple and in objects, but I also send logically deleted records to the client and optionally filter them in lists. For dropdowns I wanted that always removed without having to alter the API.

Here's my altered version, which includes searching on objects. Obviously this will only work if all your tables have an int column called Deleted (For me is part of the unique key and set to the identity value when deleted), and you pass it to the client.

At a later point I plan to alter further to allow recursive searching of objects.

App.filter('dropdownFilter', function () {
    return function (items, props: Object, keyvalue?) {
    var out = [];

        if (angular.isArray(items)) {
            items.forEach(function (item) {
                var itemMatches = false;
                var canbreak = false;

                // Filter out logically deleted records
                if (item.Deleted != 0)
                    itemMatches = false;
                else {
                    var keys = Object.keys(props);

                    for (var i = 0; i < keys.length; i++) {
                        var prop: string = keys[i];
                        var text = "";
                        // If an object, e.g. searching deep, such as Project: {ProjectName: $select.search}
                        // Then iterate through the object to find values.  
                        // NOTE: This one searches one deep from each object,
                        // e.g. Project.ProjectName
                        //      Project.User.LastName WILL NOT WORK.
                        if (angular.isObject(props[prop])) {
                            angular.forEach(props[prop], function (value, key) {
                                text = value.toLowerCase();
                                if (item[prop][key].toLowerCase().indexOf(text) !== -1) {
                                    itemMatches = true;
                                    canbreak = true;
                                }
                            });
                            if (canbreak)
                                break;
                        }
                        // If it's a simple array, then woo!
                        else {
                            text = props[prop].toLowerCase();
                            if (item[prop] != undefined && item[prop].toString().toLowerCase().indexOf(text) !== -1) {
                                itemMatches = true;
                                break;
                            }
                        }
                    }
                }

                if (itemMatches) {
                    out.push(item);
                }
            });
        }
        else {
            // Let the output be the input untouched
            out = items;
        }

    return out;
    }
});

and a sample of it in use (using select2 for angular). This is a dropdown of state/country with country as a sub-object of state.

<ui-select ng-model="personneladdress.StateID" theme="select2" class="select2">
    <ui-select-match placeholder="{{language.State}}">{{$select.selected.StateName}}</ui-select-match>
    <ui-select-choices repeat="item.StateID as item in State | dropdownFilter: {StateName: $select.search, Country: {CountryName: $select.search}}">
        <span ng-bind-html="item.StateName | highlight: $select.search"></span><br />
        <small>
            <span ng-bind-html="''+item.Country.CountryName | highlight: $select.search"></span>
        </small>
    </ui-select-choices>
</ui-select>
Share:
25,793
FrancescoMussi
Author by

FrancescoMussi

Full-stack developer based in Riga, Latvia. Hope Socrates is proud of my Socratic badge on StackOverflow.

Updated on July 18, 2020

Comments

  • FrancescoMussi
    FrancescoMussi almost 4 years

    THE SITUATION:

    I have an angular app using angular ui-select to search and select people from a database.

    It is working fine, except one thing. The user should be able to filter among the people using two criteria: name and email.

    Using the normal angular filter I am able to filter only one of them. If I try to filter both fields, it does not work anymore.

    WORKING EXAMPLE WITH ONE FIELD:

     <ui-select multiple ng-model="database_people.selectedPeople" theme="select2" ng-disabled="disabled" style="width:100%">
    
        <ui-select-match placeholder="Select person...">{{$item.name}} &lt; {{$item.email}} &gt;</ui-select-match>
    
        <ui-select-choices repeat="person2 in list_people | filter: {name: $select.search, db_data_type_id: 5}">
    
                <div ng-bind-html="person2.name | highlight: $select.search"></div>
    
                    <small>
                        email: <span ng-bind-html="''+person2.email | highlight: $select.search"></span>
                   </small>
    
        </ui-select-choices>
    
     </ui-select>
    


    NOT WORKING EXAMPLE WITH TWO FIELDS IN THE FILTER:

     <ui-select multiple ng-model="database_people.selectedPeople" theme="select2" ng-disabled="disabled" style="width:100%">
    
        <ui-select-match placeholder="Select person...">{{$item.name}} &lt; {{$item.email}} &gt;</ui-select-match>
    
        <ui-select-choices repeat="person2 in list_people | filter: {name: $select.search, email: $select.search, db_data_type_id: 5}">
    
                <div ng-bind-html="person2.name | highlight: $select.search"></div>
    
                    <small>
                        email: <span ng-bind-html="''+person2.email | highlight: $select.search"></span>
                   </small>
    
        </ui-select-choices>
    
     </ui-select>
    

    The strange thing is that it actually works BUT only for the first character. When I type the first character it highlights it in both fields, name and email. But when I type the second character it does not work anymore (I got no error in console).


    ATTEMP USING PROPSFILTER FROM ANGULAR SAMPLES:

     <ui-select multiple ng-model="database_people.selectedPeople" theme="select2" ng-disabled="disabled" style="width:100%">
    
        <ui-select-match placeholder="Select person...">{{$item.name}} &lt; {{$item.email}} &gt;</ui-select-match>
    
        <ui-select-choices repeat="person2 in list_people | propsFilter: {name: $select.search, email: $select.search, db_data_type_id: 5}">
    
                <div ng-bind-html="person2.name | highlight: $select.search"></div>
    
                    <small>
                        email: <span ng-bind-html="''+person2.email | highlight: $select.search"></span>
                   </small>
    
        </ui-select-choices>
    
     </ui-select>
    

    In this case it broke completely, there is no data anymore in the select2 and I get some errors in the console:

    Cannot read property 'toString' of null
    
    Cannot read property 'length' of undefined
    


    THE QUESTION(S):

    How can I filter among mulitple fields? Can I do that using normal filter? Or I have to use a custom filter? But in this case, why is not working properly?

    Thank you very much!