pprogramowanie;

// blog o programowaniu i branży IT

rss

Filtry

26 marca 2017, kategoria: AngularJs
filtry

Filtry w Angularze są bardzo przydatnym narzędziem. Pomagają formatować dane i są wygodnym sposobem na reużywalność kodu. Tworząc aplikację w AngularJS mamy dostępnych wiele gotowych filtrów. Jeżeli nie spełnią one naszych oczekiwań, możliwe jest dopisanie swoich. Ich główną zaletą w stosunku do zwykłych funkcji jest to, że możemy ich używać zarówno po stronie kontrolerów jak i w widokach. Sprytne operowanie filtrami potrafi znacznie skrócić kod aplikacji.

Użycie filtrów w widoku

Używanie wbudowanych filtrów jest bardzo proste. Filtr możemy dokleić do dowolnego wyrażenia, oraz niektórych dyrektyw. Przykładowe użycie wygląda następująco:

{{ wyrażenie | <nazwa_filtru> }}

Po wywołaniu powyższego kodu podczas wywołania wyrażenia zostanie dla niego zastosowany filtr. Filtry łączymy za pomocą operatora pionowej kreski (tzw. pipe).  Filtry możemy ze sobą łączyć, są one wywoływane w kolejności od lewej do prawej:

{{ wyrażenie | <filtr1> | <filtr2> | <filtr3> }}

Co ciekawe, niektóre filtry mogą przyjmować argumenty. Przekazujemy je do filtra po dwukropku.

{{ wyrażenie | <filtr1> | <filtr2>:<arg1> | <filtr3>:<arg1>:<arg2> }}

Lista dostępnych filtrów

Dla wygody programista AngularJS udostępnia wiele wbudowanych filtrów. Oto ich lista:

Filter Opis
currency Formatuje liczby do postaci waluty
date Formatuje datę według wzorca
filter Filtruje elementy kolekcję
json Wyświetla obiekt jako tekst JSON
limitTo Przycina tablice lub tekst do określonej długości
lowercase Zamienia tekst na małe litery
number Formatuje liczbę według wzorca
orderBy Sortuje po określonym parametrze
uppercase Zamienia tekst na duże litery

Przykład użycia currency

Currency jest bardzo wygodnym filtrem do formatowania walut. Jako jeden z wielu filtrów korzysta z tzw. locale z biblioteki i18n. Jeżeli dołączymy odpowiedni plik locale do projektu (np. z konfiguracją polską) wtedy domyślną walutą filtra będzie polski Złoty. Jeżeli nie dołączymy ustawień lokalizacji domyślną walutą będzie amerykański Dolar. Oto przykładowy kod:

<script>
    var app = angular.module("mainModule", []);
    
    app.controller('simpleCtrl', simpleController);
    
    function simpleController($scope) {
        $scope.kwota = 2500.25;
    }
</script>

<body ng-app="mainModule" ng-controller="simpleCtrl">
    <p></p>
    <p></p>
    <p> zł</p>
    <p>1234 zł
</body>

Efekt działania jest następujący:

currency

Zauważ jaka duża jest różnorodność osiągniętych wyników. W pierwszym przypadku wykorzystane zostały ustawienia domyślne. W drugim przypadku zmieniliśmy symbol waluty przekazując wprost parametr PLN.

Domyślnie znak dolara wyświetlany jest przed kwotą. Chcąc wyświetlić polską walutę  za liczbą, możemy posłużyć się sztuczką. Przekazujemy pusty parametr waluty a interesującą nas walutę dopisujemy bezpośrednio poza wyrażeniem. Innym lepszym rozwiązaniem byłoby dołączenie odpowiedniej konfiguracji biblioteki i18n przeznaczonej dla Polski.

Przykład użycia filter

Filter o nazwie filter służy do odfiltrowywania elementów kolekcji o określonych parametrach. Łączymy go z dyrektywą ng-repeat. Ma naprawdę sporo możliwości. Z jego pomocą łatwo można zrobić Angularową wyszukiwarkę. Oto przykładowy kod:

<script>
    var app = angular.module("mainModule", []);
    
    app.controller('simpleCtrl', simpleController);
    
    function simpleController($scope) {
        
        $scope.pracownicy = [
            {id: 1, imie: "Karol", wiek: 24},
            {id: 2, imie: "Kaja", wiek: 51},
            {id: 3, imie: "Tomek", wiek: 22},
            {id: 4, imie: "Justyna", wiek: 39},
            {id: 5, imie: "Kazek", wiek: 8}
        ]
    }
</script>

<body ng-app="mainModule" ng-controller="simpleCtrl">
    <p>Szukaj: <input type="text" ng-model="szukaj"></p>
    <table>
        <tr ng-repeat="pracownik in pracownicy | filter : szukaj track by pracownik.id">
            <td><td>
            <td> lat</td>
        </tr>
    </table>
</body>

Wynik działania jest następujący:

angular-filter

Pole wyszukiwania zbindowane jest bezpośrednio ze zmienną szukaj. Jej deklaracja nie była potrzebna w $scope ponieważ odbędzie się automatycznie. Do dyrektywy ng-repeat został natomiast dołączony filter. Filtruje on wartości właśnie po zmiennej szukaj. Zwróć uwagę, ponieważ filtry wykonują się od lewej do prawej dlatego track by musiał zostać dołączony za filtrem, aby indeksować elementy przefiltrowane a nie te, które filtr wytnie.

Filtrowanie odbywa się po wszystkich atrybutach obiektów kolekcji pracownicy. Z tego powodu wpisanie wartości 5 wyświetliło nam Kazimierza, który nie ma 5 lat – ma natomiast atrybut id z taką wartością.

Możemy filtrować kolekcję tylko po jednej wartości przesyłając do filter obiekt z nazwą atrybutu:

<tr ng-repeat="pracownik in pracownicy | filter : {imie: szukaj} track by pracownik.id">
    <td><td>
    <td> lat</td>
</tr>

Takie ustawienie spowoduje, że wyszukiwane będą tylko elementy, których atrybut imie dopasowuje się do zmiennej szukaj.

Przykład użycia json

Za pomocą filtra json możemy w bardzo prosty sposób wyświetlić dowolny obiekt JavaScript podpięty do naszego $scope. Przykładowy kod:

<script>
    var app = angular.module("mainModule", []);
    
    app.controller('simpleCtrl', simpleController);
    
    function simpleController($scope) {
        
        $scope.osoba = {
            imie: "Karol",
            wiek: 24,
            ulubionePiosenki: ['aaa', 'bbb', 'ccc']
        }
    }
</script>
 
<body ng-app="mainModule" ng-controller="simpleCtrl">
    <pre></pre>
</body>

Efekt działania:

angular-json

Przykład użycia orderBy

Filter orderBy jest niezwykle pomocnym filtrem, używanym do budowania zaawansowanych list. Możemy zastosować go do dyrektywy ng-repeat i bardzo dobrze łączy się z on z filter filter. Używając go możemy ustalić po jakim atrybucie przefiltrować kolekcję:

<script>
    var app = angular.module("mainModule", []);
    
    app.controller('simpleCtrl', simpleController);
    
    function simpleController($scope) {
        
        $scope.atrybutSortowania = 'id';
        
        $scope.osoby = [
            {id: 1, imie: "Karol", wiek: 24},
            {id: 2, imie: "Zenon", wiek: 12},
            {id: 3, imie: "Maciej", wiek: 33},
            {id: 4, imie: "Beata", wiek: 12}
        ];
        
        $scope.sortuj = function(atrybut) {
            $scope.atrybutSortowania = atrybut;
        }
    }
</script>

<body ng-app="mainModule" ng-controller="simpleCtrl">
    <table>
        <tr>
            <th ng-click="sortuj('id')">id</th>
            <th ng-click="sortuj('imie')">imie</th>
            <th ng-click="sortuj('wiek')">wiek</th>
        </tr>
        <tr ng-repeat="osoba in osoby | orderBy: atrybutSortowania track by osoba.id">
            <td></td>
            <td></td>
            <td></td>
        </tr>
    </table>
</body>

Efekt działania:

angular-orderBY

Kod jest rozbudowany, ale bardzo prosty. Filter orderBy przyjmuje jako parametr nazwę atrybutu kolekcji, po której ma sortować. Jeżeli parametr nie zostanie przekazany jako string wtedy szuka on funkcji lub zmiennej podpiętej do $scope o takiej nazwie. W naszym przypadku dodana została funkcja, ustawiająca parametr sortujący na właściwą wartość. Sortowanie odbywa się po parametrze sortującym.

Łącząc filtr orderBy z filter filter można uzyskać zaawansowaną tabelkę z możliwością sortowania kolumn i dynamicznego wyszukiwania. Dokładając do nich filter limitTo można stworzyć paginację wartości np. po 50 elementów w tabelce na stronę.

Użycie filtrów w kontrolerze

Filtrów Angularowych można użyć także w kodzie kontrolera aplikacji. W tym celu należy skorzystać z serwisu o nazwie $filter. Zwraca on instancję filtra o nazwie przekazanej jako argument. Aby móc użyć serwisu należy wstrzyknąć go jako zależność do kontrolera. Przykładowy kod wygląda następująco:

<script>
    var app = angular.module("mainModule", []);
    
    app.controller('simpleCtrl', simpleController);
    
    function simpleController($scope, $filter) {
        
        var kwota = 123456.60;
        
        $scope.kwotaFormatowana = $filter('currency')(kwota);
        
        $scope.kwotaFormatowana2 = $filter('currency')(kwota, '');
        
    }
</script>

<body ng-app="mainModule" ng-controller="simpleCtrl">
    <p></p>
    <p> zł</p>
</body>

Efekt działania:

angular-filter

Oprócz wykorzystania filtru, mamy możliwość przesyłana do niego parametrów co widać w przykładzie wyżej. Wyczyszczony został domyślny symbol waluty a następnie ręcznie w widoku dopisana waluta .

Własne filtry

Platforma AngularJS pozwala na pisanie własnych filtrów. Nie jest problemem napisanie własnej funkcji, która filtruje tablicę po określonej wartości atrybutu, jednak dzięki wprowadzeniu filtrów kod aplikacji Angular jest uporządkowany. Filtry znajdują się w filtrach, kontrolery w kontrolerach a komponenty w komponentach.

Z tego powodu zachęcam Cię, do trzymania funkcji odpowiedzialnych za filtrowanie tablic właśnie w postaci filtrów.

Aby utworzyć własny filter należy skorzystać z funkcji konstruującej wywołanej na instancji modułu. Przykładowo:

<script>
    var app = angular.module("mainModule", []);
    
    app.filter('mojFilter', function() {
        //cialo filtra
    });
</script>

Tak samo jak w przypadku kontrolerów, możemy stworzyć filter jako funkcję anonimową lub normalną:

<script>
    var app = angular.module("mainModule", []);
    
    app.filter('mojFilter', function() {
        //anonimowy
    });
    
    app.filter('mojFilter2');
    function mojFilter2() {
        //normalny
    }
</script>

Przykładowy własny filter

Stwórzmy własny filter Angularowy wyświetlający tylko ludzi wysokich. Ważną cechą filtra musi być jego skalowalność, a więc możliwość dynamicznej edycji wzrostu progowego. Kodem początkowym niech będzie następujący prosty kod:

<script>
    var app = angular.module("mainModule", []);
    
    app.controller('simpleCtrl', simpleController);
    
    function simpleController($scope, $filter) {
        $scope.osoby = [
            {id: 1, imie: "Karol", wzrost: 180},
            {id: 2, imie: "Arek", wzrost: 178},
            {id: 3, imie: "Natalia", wzrost: 172},
            {id: 4, imie: "Przemek", wzrost: 192},
        ];
    }
		
</script>

<body ng-app="mainModule" ng-controller="simpleCtrl">
    <table>
        <tr ng-repeat="osoba in osoby track by osoba.id">
            <td></td>
            <td>cm</td>
        </tr>
    </table>
</body>

Aby dodać filter należy:

Metoda filtra może w naszym wypadku wyglądać następująco:

function wysocyLudzieFilter() {
    return function(input, wzrostProgowy) {
        var przefiltrowane = [];
        
        //jezeli ktos nie podał parametru to przyjmij 180cm
        if (typeof wzrostProgowy === "undefined") {
            wzrostProgowy = 180;
        }
        
        for (var i = 0; i<input.length; i++) {
            if (input[i].wzrost >= wzrostProgowy) { //jeżeli spełnia warunek wzrostu
                przefiltrowane.push(input[i]) //to dodaj do nowej tablicy
            }
        }
        
        return przefiltrowane; //zwróć nową tablicę
    }
};

Pierwszym parametrem input jest kolekcja wejściowa, zostanie ona przesłana do filtra automatycznie. Kolejne argumenty to dodatkowe parametry filtra, które możemy zdefiniować. W powyższym kodzie jest to wzrostProgowy powyżej którego człowiek jest uznawany za wysokiego. Jeżeli nie zostanie on przekazany do filtra, wtedy przyjmijmy że jego wartość wynosi 180.

Po podpięciu funkcji do instancji filtra należy zastosować go w dyrektywie ng-repeat:

<script>
    var app = angular.module("mainModule", []);
    
    app.controller('simpleCtrl', simpleController);
    app.filter('wysocyLudzieFilter', wysocyLudzieFilter);
    
    
    function simpleController($scope, $filter) {
        $scope.wzrostMin = 180;
        
        $scope.osoby = [
            {id: 1, imie: "Karol", wzrost: 180},
            {id: 2, imie: "Arek", wzrost: 178},
            {id: 3, imie: "Natalia", wzrost: 172},
            {id: 4, imie: "Przemek", wzrost: 192},
        ];
    }
    
function wysocyLudzieFilter() {
    return function(input, wzrostProgowy) {
        var przefiltrowane = [];
        
        //jezeli ktos nie podał parametru to przyjmij 180cm
        if (typeof wzrostProgowy === "undefined") {
            wzrostProgowy = 180;
        }
        
        for (var i = 0; i<input.length; i++) {
            if (input[i].wzrost >= wzrostProgowy) { //jeżeli spełnia warunek wzrostu
                przefiltrowane.push(input[i]) //to dodaj do nowej tablicy
            }
        }
        
        return przefiltrowane; //zwróć nową tablicę
    }
};
		
</script>

<body ng-app="mainModule" ng-controller="simpleCtrl">
    <input type="number" ng-model="wzrostMin" /> <br><br>
    <table>
        <tr ng-repeat="osoba in osoby | wysocyLudzieFilter:wzrostMin track by osoba.id">
            <td></td>
            <td>cm</td>
        </tr>
    </table>
</body>

Efekt działania:

angular-wysocy