Omówię dziś tworzenie własnych filtrów. Technika ta przydatna jest zwłaszcza przy większych projektach, kiedy to nie wystarczają już podstawowe, wbudowane w framework filtry. W poprzednim wpisie opisałem korzystanie z podstawowych filtrów wbudowanych w framework – AngularJS – Podstawowe filtry. Jak wiemy z tamtego wpisu, filtry służą jedynie do modyfikowania wyświetlanej treści, a nie zawartości danych. Tak też będzie w przypadku tworzonych przez nas filtrów. Będą zmieniać wyświetlane wartości bez zmian w strukturze danych.
Trochę teorii
Filtry można podzielić na 3 typy:
- Filtry wartości,
- Filtry tablic,
- Filtry wykorzystujące inne filtry.
Filtr wartości
Pokażę, w jaki sposób można stworzyć każdy z rodzajów filtrów, zaczynając od najprostszego, czyli filtru wartości.
Zadaniem filtra będzie dodanie do filtrowanej wartości ilość znaków jaką zawiera oraz jeśli parametr jest ustawiony na wartość true, to wtedy zostanie dodany dodatkowy tekst ilość znaków wynosi:.
W ten sposób wartość Bugajsky, zostanie wyświetlona w aplikacji Bugajsky – 8 lub jeśli parametr jest ustawiony, wtedy Bugajsky – ilość znaków wynosi: 8.
Ten prosty przykład pokazuje, w jaki sposób, stworzyć prosty filtr oraz jak obsługiwać przekazywane parametry.
app.filter('valueLength', function () { return function (value, param) { if (angular.isString(value)) { if (param) { return value + ' - ilość znaków wynosi: ' + value.length; } else { return value + ' - ' + value.length; } } else { return value; } } });
Wykorzystujemy metodę filter do utworzenia nowego filtru. Jako pierwszy argument przyjmuje nazwę nowego elementu, zaś drugi to funkcja, która zwraca funkcję wykonującą określone zadania. Zwracana funkcja przyjmuje dwa argumenty: value i param. Value to nic innego jak wartość przeznaczona do filtrowania, a param to pierwszy przekazany parametr. Parametrów może być więcej, np. można by dodać jeszcze parametr nr, który odpowiadałby za usunięcie wybranej litery z przekazanego ciągu znaków. Na początku filtr sprawdza jaką wartość dostał, jeśli nie jest to string funkcja zwraca otrzymaną wartość. Do sprawdzenia przekazanej wartości wykorzystujemy Angularową metodę isString(). Następną czynnością jest sprawdzenie przekazanego parametru, jeśli wartość jest równa true, wtedy zwracany jest ciąg znaków ilość znaków wynosi: oraz długość filtrowanej wartości.
Teraz pozostaje nam wykorzystanie utworzonego filtru w kodzie html.
<p>{{$ctrl.filtr | valueLength}}</p> <p>{{$ctrl.filtr | valueLength: true}}</p>
Jak widzimy, w prosty sposób utworzyliśmy nasz pierwszy filtr, którego zadaniem było dodanie ilości znaków w przekazanej wartości oraz tekstu, który jest opcjonalny, uzależniony od parametru.
Filtr tablic
Teraz zajmiemy się filtrowaniem tablic danych. Zadanie filtru będzie takie samo jak w poprzednim przykładzie, z różnicą taką, że teraz przekazywaną daną do filtrowania nie będzie string, a tablica stringów.
app.filter('arrayLength', function () { return function (array, param) { if (angular.isArray(array)) { var data = []; if (param) { for (var i = 0; i < array.length; i++) { data.push(array[i] + ' - ilość znaków wynosi: ' + array[i].length); } } else { for (var i = 0; i < array.length; i++) { data.push(array[i] + ' - ' + array[i].length); } } return data; } else { return array; } } });
Zatem nadajemy nazwę nowemu filtrowi, tworzymy funkcję w podobny sposób jak wcześniej. Pierwszym krokiem filtra jest sprawdzenie czy przekazane dane to tablica, do tego celu znów korzystamy z Angularowej metody isArray(). Następnie tworzona jest nowa zmienna tablicowa, która będzie przechowywać przefiltrowane dane. Kolejnym krokiem jaki wykonuje filtr jest sprawdzenie jaka wartość została przekazana przez parametr param. Jeśli jest ustawiony na true, to do ilości znaków zostanie dodany tekst: ilość znaków wynosi:. W przeciwnym wypadku do wartości zostanie dodana jedynie liczba znaków w ciągu. Dodawanie danych do data odbywa się poprzez wykorzystanie pętli iterującej po przekazanej tablicy array.
Po utworzeniu filtra, musimy jeszcze wykorzystać go w pliku html.
<p ng-repeat="data in $ctrl.array | arrayLength">{{data}}</p> <p ng-repeat="data in $ctrl.array | arrayLength: true">{{data}}</p>
Filtry wykorzystujące inne filtry
Tym razem zajmiemy się stworzeniem własnego filtra, który w swoim ciele wykorzystuje już istniejące filtry. Zadaniem utworzonego filtru będzie dodanie ilości znaków w danym ciągu oraz ograniczenie rekordów za pomocą filtru limitTo.
app.filter('arrayLengthLimit', function ($filter) { return function (array, param, limit) { if (angular.isArray(array)) { var data = $filter('limitTo')(array, limit); return $filter('arrayLength')(data, param); } else { return array; } } });
Zatem w funkcji wykonującej podajemy 3 argumenty: array, param, limit. Na początek sprawdzamy czy przekazany pierwszy argument jest tablicą, w podobny sposób jak miało to miejsce przy poprzednim filtrze. Następnie tworzymy zmienną data, która otrzymuje przefiltrowane dane z użyciem filtru limitTo i parametru limit. Następnie zostanie zwrócona wartość przez funkcję po wcześniejszym przefiltrowaniu przez filtr arrayLength (napisany przez nas wcześniej filtr) z przekazaną zmienną data oraz parametrem param.
Dzięki takiemu zabiegowi tworząc nowy filtr – arrayLengthLimit nie musimy powtarzać wcześniej napisanego kodu w arrayLength.
Mając już gotowy kod pozostaje dodać filtr do danych w pliku root-app.html
<p ng-repeat="data in $ctrl.array | arrayLengthLimit:false:1">{{data}}</p> <p ng-repeat="data in $ctrl.array | arrayLengthLimit:true:1">{{data}}</p>
Kolejne parametry podajemy oddzielając je dwukropkiem.
Przykładowa aplikacja
Aplikacja składa się z dwóch plików: index.html oraz root-app.html. Komponent root-app zawiera w sobie wszystkie napisane przez nas filtry.
Plik index.html
<!DOCTYPE html> <html lang="pl" ng-app="app"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>AngularJS #12</title> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> <style> .panel-default { margin-top: 2em; padding: 1em; } </style> </head> <body> <div class="row"> <div class="col-md-4 col-md-offset-4 panel panel-default"> <root-app></root-app> </div> </div> <script src="https://code.jquery.com/jquery-3.2.1.slim.js" integrity="sha256-tA8y0XqiwnpwmOIl3SGAcFl2RvxHjA8qp0+1uCGmRmg=" crossorigin="anonymous"></script> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular.min.js"></script> <script> var app = angular.module("app", []); app.component('rootApp', { templateUrl: 'root-app.html', controller: rootAppController }); app.filter('valueLength', function () { return function (value, param) { if (angular.isString(value)) { if (param) { return value + ' - ilość znaków wynosi: ' + value.length; } else { return value + ' - ' + value.length; } } else { return value; } } }); app.filter('arrayLength', function () { return function (array, param) { if (angular.isArray(array)) { var data = []; if (param) { for (var i = 0; i < array.length; i++) { data.push(array[i] + ' - ilość znaków wynosi: ' + array[i].length); } } else { for (var i = 0; i < array.length; i++) { data.push(array[i] + ' - ' + array[i].length); } } return data; } else { return array; } } }); app.filter('arrayLengthLimit', function ($filter) { return function (array, param, limit) { if (angular.isArray(array)) { var data = $filter('limitTo')(array, limit); return $filter('arrayLength')(data, param); } else { return array; } } }); function rootAppController() { var ctrl = this; ctrl.title = 'AngularJS - Create filtrs'; ctrl.filtr = 'Bugajsky'; ctrl.array = ['Bugajsky', 'Blog']; } </script> </body> </html>
Plik root-app.html
<h1 class="text-center">{{$ctrl.title}}</h1> <h2 class="text-center">Value</h2> <p>{{$ctrl.filtr | valueLength}}</p> <p>{{$ctrl.filtr | valueLength: true}}</p> <h2 class="text-center">Array</h2> <p ng-repeat="data in $ctrl.array | arrayLength">{{data}}</p> <p ng-repeat="data in $ctrl.array | arrayLength: true">{{data}}</p> <h2 class="text-center">Filter with filters</h2> <p ng-repeat="data in $ctrl.array | arrayLengthLimit:false:1">{{data}}</p> <p ng-repeat="data in $ctrl.array | arrayLengthLimit:true:1">{{data}}</p>
Efekt działania aplikacji
Podsumowanie
Wpis ten pokazał, w jaki sposób tworzyć własne filtry, tak aby zmieniać sposób prezentacji danych bez ich modyfikowania. W niewielkich projektach wystarczają podstawowe filtry, które zostały przygotowane przez twórców frameworka AngularJS. Jednak sytuacja zmienia się, gdy mamy przed sobą większy projekt. Wtedy przydaje się widza, która została przedstawiona w tym wpisie. Najprostsze w tworzeniu są filtry operujące na stringach. Trudniejsze wydają się filtry wykorzystywane do obsługi tablic, jednak po przerobieniu kilku przykładów, również i tworzenie takich filtrów zaczyna sprawiać przyjemność. Warto również pamiętać o możliwości tworzenia filtrów wykorzystujących w swoim ciele inne filtry, dzięki czemu pozbywamy się powtarzającego się kodu.