Angular directive: opóźniona reakcja na zmianę tekstu

5

“Live search” może być bardzo ciekawym rozwiązaniem w aplikacji. Użytkownik pisze literki w textboxiku i bez wciskania entera czy guzika dostaje przefiltrowane wyniki. Tak jak to znamy z gógla chociażby.

Jeśli jednak reagować będziemy na każdą zmianę, to zanim użytkownik dostanie wynik zapytania “gdzie się podziały pieniądze z OFE” – wykonamy bardzo wiele zapytań. W takich przypadkach warto puszczać faktycznie zapytanie dopiero gdy nasz szukacz zaprzestanie pisania, czyli na przykład nie zmieni żadnej literki przez pół sekundy.

W angularze można to osiągnąć prawdopodobnie na wiele sposobów. Ja sięgnąłem po mechanizm dyrektyw i napisałem swoją, której wykorzystanie w kodzie wygląda tak:

<input obj="myFilter" delay="500" />

A sama dyrektywa:

.directive('delay', ['$timeout', function ($timeout) {
    return {
        restrict: 'A',
        scope: {
            obj: '='
        },
        link: function (scope, element, attrs) {
            var delay = attrs.delay;

            if (isNaN(parseInt(delay, 10))) {
                throw "Delay value in milliseconds must be defined";
            }
            scope.$watch('obj', function (newValue) {
                element.val(newValue);
            });

            var currentTimeout;
            element.keyup(function () {
                $timeout.cancel(currentTimeout);
                currentTimeout = $timeout(function () {
                    scope.obj = element.val();
                }, delay);
            });
        }
    };
}])

Nice.

Share.

About Author

Programista, trener, prelegent, pasjonat, blogger. Autor jedynego polskiego podcasta programistycznego: DevTalk.pl. Jeden z liderów Białostockiej Grupy .NET. Od 2008 Microsoft MVP w kategorii .NET. Więcej informacji znajdziesz na stronie O autorze. Napisz do mnie ze strony Kontakt. Dodatkowo: Twitter, Facebook.

5 Comments

  1. Nilphilus on

    w knockout jest to dostępne od razu, więc ciężko mi uwierzyć, że Angular nie ma takiej metody

    Knockout ma rateLimit i jeszcez jedną (która od 3 jest deprecated)

  2. Nilphilus,
    Może i jest (chociaż nie widzę / nie znalazłem). Ale to jest nieważne, bo jak zaczął się bawić angularem to się okazało że bardzo wiele rzeczy po prostu nie potrzebuje “wbudowanych” odpowiedników bo:
    a) łatwo jest je dopisać w elegancki sposób
    b) pisanie tego to świetna frajda :)

  3. Teodor Kulej on

    A nie lepiej w funkcji wykonywanej na callback po prostu robić wywołanie z $timeout i ew. przed wywołaniem zablokować wcześniej ustawiony timeout?

    Druga wersja, dodać dyrektywę zapisującą timeout dla konkretnych callbacków i napisać dyrektywy z których korzystamy tak, aby potrafiły zrobićttimeout trochę za naszymi plecami?

    Gdy potrzebuję zrobić coś z opóźnieniem, zwykle robie to właśnie wewnątrz callbacku, bez tworzenia osobnej dyrektywy na to.

    Nie mogę wykombinować innej rzeczy, jak przypisać model do selecta czy inputa już w runtime, z poziomu kontrolera.

  4. jest, ale jest to po prostu brzydkie w knockout:
    this.myFilter = ko.observable().extend({
    rateLimit: 500
    });

    :)