Skip to content Skip to sidebar Skip to footer

Typeahead With Results Starting With Query Ranked Higher

I'm using the AngulaJS typeahead directive like follows: typeahead='elements.name for elements in someObject.elements | filter:{name:$viewValue} | orderBy:'name' With the $viewVal

Solution 1:

I tried Ben's approach which may be right in most cases, but for me it din't work because I have recursive ng-includes and cannot make use of ng-model.

Here is how I solved my problem (it's a little of a hack, I admit, but I'd call it a clean hack):

I misuse the following filter purposely, in order to save viewValue and the property on which the filter is applied as queryObj in my typeaheadService before ordering. I also use it to filter on a property of choice.

app.filter('typeaheadFilter', ['typeaheadService', function (typeaheadService) {
    returnfunction (input, queryObj) {
        var key = Object.keys(queryObj)[0],
            query = queryObj[key];

        typeaheadService.setQueryObject(queryObj); // called once, when the viewValue changesif (!query) {
            return input;
        }
        var result = [];

        angular.forEach(input, function (object) {
            if (object[key].toLowerCase().indexOf(query.toLowerCase()) !== -1) {
                result.push(object);
            }
        });
        return result;
    };
}]);

And here is how the typeaheadService looks like (this service is used to set and get the query object for all typeaheads):

app.factory('typeaheadService', [
    function () {
        var typeaheadQueryObj;
        return {
            getQueryObject: function () {
                return typeaheadQueryObj;
            },
            setQueryObject: function (queryObj) {
                typeaheadQueryObj = queryObj;
            }
        };
    }
]);

In my MainController I add my smartOrder function to the $scope:

$scope.smartOrder = function (obj) {
    var queryObj = typeaheadService.getQueryObject(),
        key = Object.keys(queryObj)[0],
        query = queryObj[key];
    if (obj[key].toLowerCase().indexOf(query.toLowerCase()) === 0) {
        return ('a' + obj[key]);
    }
    return ('b' + obj[key]);
};

Finally, my typeahead:

typeahead="element.name for element in elements | typeaheadFilter:{name:$viewValue} | orderBy:smartOrder | limitTo:dropdownMenuLimit"

Solution 2:

You would need to change the orderBy, not the filter. Maybe doing a function instead of a property on the orderBy would achieve the desired effect.

Possibly something like

$scope.typeAheadModel = '';
$scope.orderByPriority = function (element) {
    return element.name.indexOf($scope.typeAheadModel);
}

And in your HTML add an ng-model on your input

ng-model="typeAheadModel" typeahead="element.name for element in someObject.elements | filter:{name:$viewValue} | orderBy:orderByPriority

edit

Here's a plunkr of it, for some reason it doesn't do it properly when you type in just "f", but once you type in "fo" it orders it correctly. Not sure why.

Solution 3:

Bens Approach is not bad, it was useful to me, but it has a problem if your list contains uppercases.

so you have to transform the two endpoints:

$scope.typeAheadModel = '';

$scope.orderByPriority = function (element) {
    var lower_model = $scope.typeAheadModel.toLowerCase();
    var lower_name = element.name.toLowerCase();

    return lower_name.indexOf(lower_model);
}

Post a Comment for "Typeahead With Results Starting With Query Ranked Higher"