在AngularJS的选择下拉列表中添加搜索过滤器

时间:2014-08-28 13:47:48

标签: html5 angularjs

我想在angularJS的选择下拉列表中添加搜索过滤器。

我已经使用ng-options列出选项并使用过滤器过滤掉搜索框中的数据,但问题是搜索框没有进入(或下方)选择下拉列表。 (当我单击选择下拉列表时,它会显示一个搜索过滤器,下面会显示所有选项)

以下是供您参考的代码:

<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>

4 个答案:

答案 0 :(得分:8)

我已经在课程中使用了引导按钮&#39; dropdown-toggle&#39;然后点击按钮,我附加了一个输入搜索框,如下所示:

<div class="dropdown pull-right makePaymentDropdownMainDiv" auto-close="outsideClick">
<button class="btn btn-default dropdown-toggle makePaymentDropdownBtn" type="button" id="individualDrop" data-toggle="dropdown">{{payment.entity}}<span class="caret pull-right"></span></button>
<span ng-show="submitted"><label class="error">Select an Individual</label></span>
<ul class="dropdown-menu makePaymentDropdownUlStyle" role="menu" aria-labelledby="individualDrop">
    <input disable-auto-close type="search" ng-model="serchFilter" class="makePaymentDropdownSearchBox" placeholder="Search"></input>
    <li role="presentation" ng-repeat="indi in individuals | filter: serchFilter"><a role="menuitem" ng-click="selectEntity(indi)">{{indi}}</a></li>  
</ul>
</div>

显示&#39; li&#39;使用ng-repeat。

请记住添加auto-close =&#34; outsideClick&#34;到你的下拉列表,以便它不会在过滤尝试时关闭。

答案 1 :(得分:6)

对不起,我已经很晚了,但对我来说,听起来你需要acute-select,一个Angular的开源扩展(麻省理工学院许可证)就是这样,没有进一步的依赖。

他们还有一个demo page,它可以很好地展示它的功能。

答案 2 :(得分:3)

您可以使用简单且最佳的方式在AngularJS的选择下拉列表中搜索过滤器

工作演示: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>

答案 3 :(得分:-2)

https://docs.angularjs.org/api/ng/directive/select

ngOption中只能有一个硬编码。