Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Add search filter inside the select dropdown in AngularJS

Tags:

html

angularjs

I want to add a search filter inside a select dropdown in angularJS.

I have used ng-options to list down the options and used filter to filter out the data in the search box , but the problem is that the search box is not coming inside(or under) select dropdown. (When I click the select dropdown, it shows a search filter and below it has all the options)

Below is the code for your reference :

<div class="rowMargin">
<label class="control-label" for="entitySel">Entity:</label>
<div class="controls">
    <select id="entityId" class="input-medium" type="text" name="entityId" ng-model="payment.entityId" ng-options="entityOpt for entityOpt in paymentEntityOptions">
        <option value="">Select</option>
    </select>
    <span ng-show=" submitted && addPayment.entityId.$error.required">
        <label class="error">Please provide entity Id </label>
    </span>
    <div ng-show="payment.entityId == \'Individual\'">
        <span>
            <select ng-model="payment.entity.individual" ng-options = "individual for individual in individualEntities | filter : filterEntity">
                <option value="">Select Individual Entity</option>
                <option>
                    <input type="search" placeholder="Search" ng-model="filterEntity"></input>
                </option>
            </select>
        </span>
    </div>
    <div ng-show="payment.entityId == \'Group\'">
        <span>
            <select ng-model="payment.entity.group" ng-options = "group for group in groupEntities | filter : filterEntity">
                <option value="">Select Group Entity</option>
                <input type="search" placeholder="Search" ng-model="filterEntity"></input>
            </select>
        </span>
    </div>
</div>

like image 343
Vaibhav Pachauri Avatar asked Aug 28 '14 13:08

Vaibhav Pachauri


People also ask

How do I add a search option to a drop down list?

Use any element to open the dropdown menu, e.g. a <button>, <a> or <p> element. Use a container element (like <div>) to create the dropdown menu and add the dropdown links inside it. Wrap a <div> element around the button and the <div> to position the dropdown menu correctly with CSS.

Which one is correct way to apply filters in AngularJS?

In AngularJS, you can also inject the $filter service within the controller and can use it with the following syntax for the filter. Syntax: $filter("filter")(array, expression, compare, propertyKey) function myCtrl($scope, $filter) { $scope. finalResult = $filter("filter")( $scope.

How do you filter an HTML table based on drop down selected value?

In this case, you need to get all the data from the server into something you can run filtering logic on like a JavaScript object (usually using AJAX to get JSON data). After you got the data and filtered it you can use JavaScript to dynamically create the table rows and update the pagination data.


1 Answers

you can use easy and best way to search filter inside the select dropdown in AngularJS

Working Demo : http://plnkr.co/edit/o767Mg6fQoyc7jKq77If?p=preview

(function (angular, undefined) {
    'use strict';

    // TODO: Move to polyfill?
    if (!String.prototype.trim) {
        String.prototype.trim = function () {
            return this.replace(/^\s+|\s+$/g, '');
        };
    }

    /**
     * A replacement utility for internationalization very similar to sprintf.
     *
     * @param replace {mixed} The tokens to replace depends on type
     *  string: all instances of $0 will be replaced
     *  array: each instance of $0, $1, $2 etc. will be placed with each array item in corresponding order
     *  object: all attributes will be iterated through, with :key being replaced with its corresponding value
     * @return string
     *
     * @example: 'Hello :name, how are you :day'.format({ name:'John', day:'Today' })
     * @example: 'Records $0 to $1 out of $2 total'.format(['10', '20', '3000'])
     * @example: '$0 agrees to all mentions $0 makes in the event that $0 hits a tree while $0 is driving drunk'.format('Bob')
     */
    function format(value, replace) {
        if (!value) {
            return value;
        }
        var target = value.toString();
        if (replace === undefined) {
            return target;
        }
        if (!angular.isArray(replace) && !angular.isObject(replace)) {
            return target.split('$0').join(replace);
        }
        var token = angular.isArray(replace) && '$' || ':';

        angular.forEach(replace, function (value, key) {
            target = target.split(token + key).join(value);
        });
        return target;
    }

    var module = angular.module('AxelSoft', []);

    module.value('customSelectDefaults', {
        displayText: 'Select...',
        emptyListText: 'There are no items to display',
        emptySearchResultText: 'No results match "$0"',
        addText: 'Add',
        searchDelay: 300
    });

    module.directive('customSelect', ['$parse', '$compile', '$timeout', '$q', 'customSelectDefaults', function ($parse, $compile, $timeout, $q, baseOptions) {
        var CS_OPTIONS_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?$/;

        return {
            restrict: 'A',
            require: 'ngModel',
            link: function (scope, elem, attrs, controller) {
                var customSelect = attrs.customSelect;
                if (!customSelect) {
                    throw new Error('Expected custom-select attribute value.');
                }

                var match = customSelect.match(CS_OPTIONS_REGEXP);

                if (!match) {
                    throw new Error("Expected expression in form of " +
                        "'_select_ (as _label_)? for _value_ in _collection_[ track by _id_]'" +
                        " but got '" + customSelect + "'.");
                }

                elem.addClass('dropdown custom-select');

                // Ng-Options break down
                var displayFn = $parse(match[2] || match[1]),
                    valueName = match[3],
                    valueFn = $parse(match[2] ? match[1] : valueName),
                    values = match[4],
                    valuesFn = $parse(values),
                    track = match[5],
                    trackByExpr = track ? " track by " + track : "",
                    dependsOn = attrs.csDependsOn;

                var options = getOptions(),
                    timeoutHandle,
                    lastSearch = '',
                    focusedIndex = -1,
                        matchMap = {};

                var itemTemplate = elem.html().trim() || '{{' + (match[2] || match[1]) + '}}',

                    dropdownTemplate =
                    '<a class="dropdown-toggle" data-toggle="dropdown" href ng-class="{ disabled: disabled }">' +
                        '<span>{{displayText}}</span>' +
                        '<b></b>' +
                    '</a>' +
                    '<div class="dropdown-menu">' +
                        '<div stop-propagation="click" class="custom-select-search">' +
                            '<input class="' + attrs.selectClass + '" type="text" autocomplete="off" ng-model="searchTerm" />' +
                        '</div>' +
                        '<ul role="menu">' +
                            '<li role="presentation" ng-repeat="' + valueName + ' in matches' + trackByExpr + '">' +
                                '<a role="menuitem" tabindex="-1" href ng-click="select(' + valueName + ')">' +
                                    itemTemplate +
                                '</a>' +
                            '</li>' +
                            '<li ng-hide="matches.length" class="empty-result" stop-propagation="click">' +
                                '<em class="muted">' +
                                    '<span ng-hide="searchTerm">{{emptyListText}}</span>' +
                                    '<span class="word-break" ng-show="searchTerm">{{ format(emptySearchResultText, searchTerm) }}</span>' +
                                '</em>' +
                            '</li>' +
                        '</ul>' +
                        '<div class="custom-select-action">' +
                            (typeof options.onAdd === "function" ?
                            '<button type="button" class="btn btn-primary btn-block add-button" ng-click="add()">{{addText}}</button>' : '') +
                        '</div>' +
                    '</div>';

                // Clear element contents
                elem.empty();

                // Create dropdown element
                var dropdownElement = angular.element(dropdownTemplate),
                    anchorElement = dropdownElement.eq(0).dropdown(),
                    inputElement = dropdownElement.eq(1).find(':text'),
                    ulElement = dropdownElement.eq(1).find('ul');

                // Create child scope for input and dropdown
                var childScope = scope.$new(true);
                configChildScope();

                // Click event handler to set initial values and focus when the dropdown is shown
                anchorElement.on('click', function (event) {
                    if (childScope.disabled) {
                        return;
                    }
                    childScope.$apply(function () {
                        lastSearch = '';
                        childScope.searchTerm = '';
                    });

                    focusedIndex = -1;
                    inputElement.focus();

                    // If filter is not async, perform search in case model changed
                    if (!options.async) {
                        getMatches('');
                    }
                });

                if (dependsOn) {
                    scope.$watch(dependsOn, function (newVal, oldVal) {
                        if (newVal !== oldVal) {
                            childScope.matches = [];
                            childScope.select(undefined);
                        }
                    });
                }

                // Event handler for key press (when the user types a character while focus is on the anchor element)
                anchorElement.on('keypress', function (event) {
                    if (!(event.altKey || event.ctrlKey)) {
                        anchorElement.click();
                    }
                });

                // Event handler for Esc, Enter, Tab and Down keys on input search
                inputElement.on('keydown', function (event) {
                    if (!/(13|27|40|^9$)/.test(event.keyCode)) return;
                    event.preventDefault();
                    event.stopPropagation();

                    switch (event.keyCode) {
                        case 27: // Esc
                            anchorElement.dropdown('toggle');
                            break;
                        case 13: // Enter
                            selectFromInput();
                            break;
                        case 40: // Down
                            focusFirst();
                            break;
                        case 9:// Tab
                            anchorElement.dropdown('toggle');
                            break;
                    }
                });

                // Event handler for Up and Down keys on dropdown menu
                ulElement.on('keydown', function (event) {
                    if (!/(38|40)/.test(event.keyCode)) return;
                    event.preventDefault();
                    event.stopPropagation();

                    var items = ulElement.find('li > a');

                    if (!items.length) return;
                    if (event.keyCode == 38) focusedIndex--;                                    // up
                    if (event.keyCode == 40 && focusedIndex < items.length - 1) focusedIndex++; // down
                    //if (!~focusedIndex) focusedIndex = 0;

                    if (focusedIndex >= 0) {
                        items.eq(focusedIndex)
                            .focus();
                    } else {
                        focusedIndex = -1;
                        inputElement.focus();
                    }
                });

                resetMatches();

                // Compile template against child scope
                $compile(dropdownElement)(childScope);
                elem.append(dropdownElement);

                // When model changes outside of the control, update the display text
                controller.$render = function () {
                    setDisplayText();
                };

                // Watch for changes in the default display text
                childScope.$watch(getDisplayText, setDisplayText);

                childScope.$watch(function () { return elem.attr('disabled'); }, function (value) {
                    childScope.disabled = value;
                });

                childScope.$watch('searchTerm', function (newValue) {
                    if (timeoutHandle) {
                        $timeout.cancel(timeoutHandle);
                    }

                    var term = (newValue || '').trim();
                    timeoutHandle = $timeout(function () {
                        getMatches(term);
                    },
                    // If empty string, do not delay
                    (term && options.searchDelay) || 0);
                });

                // Support for autofocus
                if ('autofocus' in attrs) {
                    anchorElement.focus();
                }

                var needsDisplayText;
                function setDisplayText() {
                    var locals = { };
                    locals[valueName] = controller.$modelValue;
                    var text = displayFn(scope, locals);

                    if (text === undefined) {
                        var map = matchMap[hashKey(controller.$modelValue)];
                        if (map) {
                            text = map.label;
                        }
                    }

                    needsDisplayText = !text;
                    childScope.displayText = text || options.displayText;
                }

                function getOptions() {
                    return angular.extend({}, baseOptions, scope.$eval(attrs.customSelectOptions));
                }

                function getDisplayText() {
                    options = getOptions();
                    return options.displayText;
                }

                function focusFirst() {
                    var opts = ulElement.find('li > a');
                    if (opts.length > 0) {
                        focusedIndex = 0;
                        opts.eq(0).focus();
                    }
                }

                // Selects the first element on the list when the user presses Enter inside the search input
                function selectFromInput() {
                    var opts = ulElement.find('li > a');
                    if (opts.length > 0) {
                        var ngRepeatItem = opts.eq(0).scope();
                        var item = ngRepeatItem[valueName];
                        childScope.$apply(function () {
                            childScope.select(item);
                        });
                        anchorElement.dropdown('toggle');
                    }
                }

                function getMatches(searchTerm) {
                    var locals = { $searchTerm: searchTerm }
                    $q.when(valuesFn(scope, locals)).then(function (matches) {
                        if (!matches) return;

                        if (searchTerm === inputElement.val().trim()/* && hasFocus*/) {
                            matchMap = {};
                            childScope.matches.length = 0;
                            for (var i = 0; i < matches.length; i++) {
                                locals[valueName] = matches[i];
                                var value = valueFn(scope, locals),
                                    label = displayFn(scope, locals);

                                matchMap[hashKey(value)] = {
                                    value: value,
                                    label: label/*,
                                    model: matches[i]*/
                                };

                                childScope.matches.push(matches[i]);
                            }
                            //childScope.matches = matches;
                        }

                        if (needsDisplayText) setDisplayText();
                    }, function() {
                        resetMatches();
                    });
                }

                function resetMatches() {
                    childScope.matches = [];
                    focusedIndex = -1;
                };

                function configChildScope() {
                    childScope.addText = options.addText;
                    childScope.emptySearchResultText = options.emptySearchResultText;
                    childScope.emptyListText = options.emptyListText;

                    childScope.select = function (item) {
                        var locals = {};
                        locals[valueName] = item;
                        var value = valueFn(childScope, locals);
                        //setDisplayText(displayFn(scope, locals));
                        childScope.displayText = displayFn(childScope, locals) || options.displayText;
                        controller.$setViewValue(value);

                        anchorElement.focus();

                        typeof options.onSelect === "function" && options.onSelect(item);
                    };

                    childScope.add = function () {
                        $q.when(options.onAdd(), function (item) {
                            if (!item) return;

                            var locals = {};
                            locals[valueName] = item;
                            var value = valueFn(scope, locals),
                                label = displayFn(scope, locals);

                            matchMap[hashKey(value)] = {
                                value: value,
                                label: label/*,
                                    model: matches[i]*/
                            };

                            childScope.matches.push(item);
                            childScope.select(item);
                        });
                    };

                    childScope.format = format;

                    setDisplayText();
                }

                var current = 0;
                function hashKey(obj) {
                    if (obj === undefined) return 'undefined';

                    var objType = typeof obj,
                        key;

                    if (objType == 'object' && obj !== null) {
                        if (typeof (key = obj.$$hashKey) == 'function') {
                            // must invoke on object to keep the right this
                            key = obj.$$hashKey();
                        } else if (key === undefined) {
                            key = obj.$$hashKey = 'cs-' + (current++);
                        }
                    } else {
                        key = obj;
                    }

                    return objType + ':' + key;
                }
            }
        };
    }]);

    module.directive('stopPropagation', function () {
        return {
            restrict: 'A',
            link: function (scope, elem, attrs, ctrl) {
                var events = attrs['stopPropagation'];
                elem.bind(events, function (event) {
                    event.stopPropagation();
                });
            }
        };
    });
})(angular);

<body ng-app="Demo">
        <div class="container" ng-controller="DemoController">
            <label>Level 1</label>
            <div custom-select="g for g in nestedItemsLevel1 | filter: $searchTerm" custom-select-options="level1Options" ng-model="level1"></div>
            <label>Level 2</label>
            <div custom-select="g for g in nestedItemsLevel2 | filter: $searchTerm" ng-model="level2" cs-depends-on="level1"></div>
        </div>
        <!-- basic scripts -->

        <!--[if !IE]> -->

        <script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
        <!-- <![endif]-->

        <!--[if IE]>
        <script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
        <![endif]-->

        <script src="http://maxcdn.bootstrapcdn.com/bootstrap/2.3.2/js/bootstrap.min.js"></script>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.3/angular.min.js"></script>
        <script src="js/customSelect.js"></script>
            <script>
                (function () {
                    var app = angular.module('Demo', ['AxelSoft']);

                    app.controller('DemoController', ['$scope', '$timeout', '$q', function ($scope, $timeout, $q) {
                        $scope.searchAsync = function (term) {
                                // No search term: return initial items
                            if (!term) {
                                return  ['Item 1', 'Item 2', 'Item 3'];
                            }
                            var deferred = $q.defer();
                            $timeout(function () {
                                var result = [];
                                for (var i = 1; i <= 3; i++)
                                {
                                    result.push(term + ' ' + i);
                                }
                                deferred.resolve(result);
                            }, 300);
                            return deferred.promise;
                        };
                        $scope.nestedItemsLevel1 = ['Item 1', 'Item 2', 'Item 3'];
                        $scope.level1 = $scope.nestedItemsLevel1[0];
                        $scope.level1Options = {
                            onSelect: function (item) {
                                var items = [];
                                for (var i = 1; i <= 5; i++) {
                                    items.push(item + ': ' + 'Nested ' + i);
                                }
                                $scope.nestedItemsLevel2 = items;
                            }
                        };
                        $scope.nestedItemsLevel2 = [];
                        $scope.level1Options.onSelect($scope.nestedItemsLevel1[0]);
                    }]);

                })();
            </script>
    </body>
like image 61
SantoshK Avatar answered Sep 21 '22 01:09

SantoshK