AngularJS filtry&moduly

JavaScript AngularJS AngularJS filtry&moduly

V úvodu o AngularJS jsme se dozvěděli, jak je lehké naprogramovat webovou aplikaci v AngularJS. Dnes si povíme něco o tom, jak filtrovat výstup v naší šabloně HTML a také jak vytvořit takový vlastní filtr.

V AngularJS můžeme filtrovat buďto repeatery (tedy direktivu ngRepeat) nebo samotné hodnoty a to nejen v šabloně, ale třeba i v controlleru nebo vlastní direktivě. Základní použití filtru v šabloně je následující.

{{ hodnota | filtr:parametr1:parametr2 | dalsiFiltr:parametr1 }}

Takto můžeme zavolat filter v JavaScruiptu. Proměnná $filter je servis předaný třeba controlleru, direktivě nebo něčemu jinému. Podobně jako u $scope tuto hodnotu sami nepředáváme. O to se postará DI subsystém angularu.

$filter(jméno);

$filter vrací většinou funkci, takže můžeme hned filtr zavolat např. $filter(jméno)(parametr1, parametr2)

Filtrování repeaterů

Filtrování repeaterů jsme si v rychlosti ukázali v minulém tutorálu. Teď si ho rozebereme trochu podrobněji. Framework nám nabízí pár vestavěných filtrů. Jsou jimi orderBy, filter a limitTo.

$filter('filter')(po­le, výraz) - filter:výraz

Filtr filer očekává jediný parametr. Je jím hodnota podle které se má výsledek filtrovat. Zobrazí se pak jen položky, které odpovídají danému filtru. Mějme třeba pole uživatel, ve kterém budou všichni uživatelé Devbooku. Pokud bychom chtěli udělat označení členů s automatickým dokončováním, bylo by velmi nepraktické pod inputem zobrazit celý seznam těchto uživatel. Právě tento seznam odfiltrujeme textem, který je v inputu, a tak pokud návštěvník zadá 'sd', zobrazí se mu jen uživatelé, kteří mají ve svém nicku výraz 'sd'.

Parametry
  • Pole - pole které chceme filtrovat
  • Výraz - {textový řetězec|objek­t|funkce} - výraz podle kterého budeme pole filtrovat
  • - textový řetězec: v poli zůstanou jen položky, které obsahují daný výraz. Všechny tyto položky budou vráceny samotným filterm jako nové pole
  • - objekt: Objekt můžeme použít, abychom filtrovali jen specifické oblasti pole. Pokud pole obsahuje objekty s více hodnotami (např. nick, web, telefon atp.), můžeme repeater filtrovat jen podle některé z nich nebo podle každé s jinými hodnotami. Např. výraz { nick: "sd", web: "devbook" } by se pojil se všemi uživateli v poli s nickem obsahujícím 'sd' a webovou stránkou obsahující 'devbook'. Název vlastnosti objektu (web, nick) můžeme nahradit znakem $ (dolar), který se pojí se všemi vlastnostmi objektu. Výraz { $: "sd" } má stejný efekt, jako výše uvedený příklad (tedy jen 'sd').
  • - funkce: pokud repeateru předáme funkci, bude zavolána pro každý element s určitými parametry. Filtr v tomto případě vrátí všechny prvky, u kterých funkce vrátila logickou hodnotu true
Příklad
<!DOCTYPE html>
<html ng-app>
    <head>
        <meta charset="utf8" />
        <script src="http://code.angularjs.org/angular-1.0.1.min.js"></script>
    </head>
    <body>
        <div ng-init="uzivatele = [{jmeno:'John', prijmeni:'Doe', email:'john@doe.com'},
                                    {jmeno:'Jane', prijmeni:'Doe', email:'jane@doe.com'},
                                    {jmeno:'Joe', prijmeni:'Blow', email:'joe@blow.com'},
                                    {jmeno:'Chan', prijmeni:'Siu Ming', email:'chan@siu-ming.com'}]"></div>

        Hledání: <input ng-model="searchText" />
        <table>
            <tr> <th>Jméno</th><th>Příjmení</th><th>E-mail</th> <tr>
            <tr ng-repeat="uzivatel in uzivatele | filter:searchText">
                <td>{{uzivatel.jmeno}}</td>
                <td>{{uzivatel.prijmeni}}</td>
                <td>{{uzivatel.email}}</td>
            <tr>
        </table><hr />

        Vše: <input ng-model="search.$" /> <br />
        Jen jméno <input ng-model="search.jmeno" /><br />
        Jen příjmení <input ng-model="search.prijmeni" /><br />
        Jen e-mail <input ng-model="search.email" /><br />
        <table>
            <tr> <th>Jméno</th><th>Příjmení</th><th>E-mail</th> <tr>
            <tr ng-repeat="uzivatel in uzivatele | filter:search">
                <td>{{uzivatel.jmeno}}</td>
                <td>{{uzivatel.pijmeni}}</td>
                <td>{{uzivatel.email}}</td>
            <tr>
        </table>
    </body>
</html>

Podobný příklad najdete i v oficiální API dokumentaci AngularJS

$filter('order­By')(pole, výraz[, převrátit]) - orderBy:výraz[:pře­vrátit]

Název tohoto filtru mluví za vše. Tento filtr pole seřadí podle zadaných hodnot. Ty jsou dvě. Jednak výraz, podle kterého chceme pole seřadit a pak jestli chceme pole seřadit vzestupně nebo sestupně.

Parametry
  • Pole - pole které chceme seřadit
  • Výraz - {funkce|textový řetězec|pole funkcí, textových řetězců} - výraz podle kterého budeme pole řadit
  • - funkce: Zavolá se pro každý prvek pole. Výsledek se bude řadit podle výstupu operátory < (menší než), = (rovno), > (větší než)
  • - textový řetězec: AngularJS váraz, který se shoduje s názvy vlastností objektu v poli (např. nick, web, telefon). Lze použít prefix + nebo - pokud chcete pole seřadit vzestupně (+) nebo sestupně (-)
  • - pole: Pole funkcí nebo textových řetězců. První z hodnot je použita vždy. Pokud jsou v poli dvě stejné hodnoty, je použita další položka tohoto pole k seřazení.
  • Převrátit - {logická hodnota} - převrátí výsledek
Příklad
<!DOCTYPE html>
<html ng-app>
    <head>
        <meta charset="utf8" />
        <script src="http://code.angularjs.org/angular-1.0.1.min.js"></script>
    </head>
    <body>
        <div ng-init="uzivatele = [{jmeno:'John', prijmeni:'Doe', email:'john@doe.com'},
                                    {jmeno:'Jane', prijmeni:'Doe', email:'jane@doe.com'},
                                    {jmeno:'Joe', prijmeni:'Blow', email:'joe@blow.com'},
                                    {jmeno:'Chan', prijmeni:'Siu Ming', email:'chan@siu-ming.com'}]">
            <pre>Řadit podle = {{predicate}}; převrátit = {{reverse}}</pre>
            <hr/>
            [ <a href="" ng-click="predicate=''">neseřazeno</a> ]
            <table>
                <tr>
                    <th><a href="" ng-click="predicate = 'jmeno'; reverse=false">Jméno</a>
                        (<a href ng-click="predicate = '-jmeno'; reverse=false">^</a>)</th>
                    <th><a href="" ng-click="predicate = 'prijmeno'; reverse=!reverse">Příjmení</a></th>
                    <th><a href="" ng-click="predicate = 'email'; reverse=!reverse">E-mail</a></th>
                <tr>
                <tr ng-repeat="uzivatel in uzivatele | orderBy:predicate:reverse">
                    <td>{{uzivatel.jmeno}}</td>
                    <td>{{uzivatel.prijmeni}}</td>
                    <td>{{uzivatel.email}}</td>
                <tr>
            </table>
        </div>
    </body>
</html>

$filter('limit­To')(pole, výraz) - limitTo:výraz

Filtr limitTo omezí repeater jen na daný počet výpisů od začátku pole. Ve výrazu může být číslo. Pokud uvedeme záporné číslo, budou se brát položky od konce daného pole.

Příklad
<!DOCTYPE html>
<html ng-app>
    <head>
        <meta charset="utf8" />
        <script src="http://code.angularjs.org/angular-1.0.1.min.js"></script>
    </head>
    <body>
        <div ng-init="uzivatele = [1,2,3,4,5,6,7,8,9,10]; limit = 3;">
            Limit {{uzivatele}}: <input type="text" ng-model="limit" /><br />
            Výstup: {{uzivatele | limitTo:limit }}
        </div>
    </body>
</html>

Filtrování hodnot

Dále můžete filtrovat hodnoty. V AngularJS je pár vestavěných filtrů uvedených v seznamu pod tímto odstavcem. Filtry lze volat v JS ze servisu $filter nebo v šabloně klasickým zápisem filtrů. Obecný zápis obou těchto způsobů je uveden výše.

  • currency: Zformátuje číslo jako měnu a vloží před něj symbol, který očekává v parametru. Zformátování čísla by tedy mohlo vypadat následovně:
{{ 1235 | currency['$'] }}

Výsledek: $1,235.00

  • date: Zformátuje timestamp nebo datum na zadaný formát data a času. Jako jediný parametr očekává formát, do kterého má timestamp zformátovat. Přehled najdete na adrese http://docs.angularjs.org/….filter:date Příklad použití filtru date:
{{ 1288323623006 | 'dd.M.yyyy H:m:s' }}

Výsledek: 29.10.2010 05:40:23

  • json: Zformátuje JavaScriptový objekt jako řetězec textu JSON. Příklad:
{{ { nick: "someone" } | json }}

Výsledek:

{
   "nick": "someone"
}
  • lowercase, uppercase: První převede text na malé znaky, druhý na velké.
{{ "DEVBOOK" | lowercase }}
{{ "devbook" | uppercase }}

Výsledek: devbook DEVBOOK

  • number: Zformátuje číslo. Jako parametr očekává číslo (počet desetinných míst, která má zobrazit)
{{ 1234.56789 | number:2 }}

Výsledek: 1,234.56

Tak to byly všechny základní předdefinované filtry v AngularJS. Někdy se může stát, že potřebujeme výstup (repeater nebo hodnotu) filtrovat jinak. V takové případě nám AngularJS nabízí vytvořit filtr vlastní. Jeden takový filtr, který bývá často potřeba, je filtr na zkracování textu. Pokud ale chceme definovat vlastní filtry, musíme si nejprve říct něco o modulech.

Moduly

Moduly sdružují filtry, direktivy a kontrolery, které patří k sobě. Dají se chápat jako takové namespace pro aplikaci. Moduly vytváří angular.module(jméno, vyžadované, konfigurační_fce). V parametrech je jméno modulu, což je textový řetězec, pole vyžadovaných modulů (jejich jmen - textových řetězců), které patří do tohoto modulu a které budeme v aplikaci používat. Poslední parametr je konfigurační funkce, o které si povíme víc v jiném díle seriálu. V parametrech je nepovinná.

Takže jak to souvisí s filtry? Je to prosté, filtr musí patřit do nějakého modulu, proto ho nejdřív musíme vytvořit. V externím JS souboru si tedy vytvoříme hlavní modul naší aplikace, který nazveme třeba testApp. Jméno hlavního modulu nesmíme zapomenout uvést v direktivě ngApp. Samotný modul ale ponecháme prázdný. V druhém parametru mu poze předáme jméno našeho druhého modulu testApp.filtry, kam vložíme náš filtr. Samotný filtr pak vytvoří module.filter, který má dva parametry, jméno filtru a továrnu, která musí vrátit modifikační funkci. Kód samotného filtru je pak velmi krátký a velmi užitečný, jak je zvykem v AngularJS. Filtr a zápis modulů by mohl vypadat například takto:

// Hlavní modul aplikace
angular.module('testApp', ['testApp.filtry']);

// Modul pro filtry
angular.module('testApp.filtry', [])
    .filter('truncate', function() {
        return function(text, delka, znaky) {

            // Pokud nedostaneme textový řetězec, nebudeme dělat nic
            if (!angular.isString(text)) return;

            // Vstupní parametry
            if (!angular.isNumber(delka))
                delka = 15;
            if (!angular.isString(znaky))
                znaky = "...";

            // Pokud je délka textu menší než délka v parametru, neprovedeme žádnou změnu
            if (text.length <= delka)
                return text;
            // Jinak text zkrátíme a přidáme za něj dané znaky
            return text.substring(0, delka) + znaky;
        };
    });

Všimněte si také, že AngularJS obsahuje funkce na kontrolu typů. Můžete samozřejmě použít operátor typeof, nicméně způsob angularu je čitelnější a nemusíte už nic vymýšlet. Nakonec nezapomeňte přidat do ngApp jméno hlavního modulu testApp a pak již můžete používat filtr truncate.

<!DOCTYPE html>
<html ng-app="testApp">
    <head>
        <meta charset="utf8" />
        <script src="http://code.angularjs.org/angular-1.0.1.min.js"></script>
        <script src="js/scripts.js"></script>
    </head>
    <body>
        <section>
            Textový řetězec: <input type="text" ng-model="text" /><br />
            <h4>Výstup po 25 znaků:</h4>
            <p>{{text|truncate:25}}</p>
            <h4>Výstup po 10 znaků:</h4>
            <p>{{text|truncate:10}}</p>
            <h4>Výstup po 5 znaků s ukončením ' =>':</h4>
            <p>{{text|truncate:5:' =>'}}</p>
        </section>
    </body>
</html>

Příště si řekneme něco o direktivách a jak takovou vlastní direktivu vytvořit.


 

Stáhnout

Staženo 441x (991 B)
Aplikace je včetně zdrojových kódů v jazyce JavaScript

 

  Aktivity (1)

Článek pro vás napsal Drahomír Hanák
Avatar
Autor v současné době studuje Informatiku. Zajímá se o programování, matematiku a grafiku.

Jak se ti líbí článek?
Celkem (3 hlasů) :
55555


 


Miniatura
Předchozí článek
Úvod do AngularJS
Miniatura
Všechny články v sekci
AngularJS

 

 

Komentáře

Avatar
Milan Gallas
Redaktor
Avatar
Milan Gallas:

Ahoj, mám problém. Když porovnávám produkty podle ceny vzestupně. Tak bych očekával následující pořadí

305, 850, 910, 1030, 1190

ale místo tohoto výsledku dostanu následující pořadí

1030, 1180, 305, 850, 910

Nevíš čím to může být?

 
Odpovědět 1.2.2014 20:02
Avatar
Drahomír Hanák
Tým ITnetwork
Avatar
Odpovídá na Milan Gallas
Drahomír Hanák:

Ahoj, ten filter orderBy má 3 argumenty (včetně pole). Pokud je třetí argument true, bude je řadit sestupně (od největšího po nejmenší). Tzn. volání reverse=!reverse po kliknutí nejdřív setřídí prvky sestupně a až pak vzestupně.

 
Odpovědět 1.2.2014 20:19
Avatar
Milan Gallas
Redaktor
Avatar
Odpovídá na Drahomír Hanák
Milan Gallas:

Jo ale já mám úplně jiný problém. Mám ceny produktů. Řekněme jenom dvě ceny, kvůli jednoduchosti. (1090 Kč a 309 Kč). Když je dám seřadit od nejmenšího tak se mi seřadí následovně

1090, 309

Myslím, že se ty čísla berou jako textový řetězec, né jako čísla. Malé přirovnání. Vytvoř si 2 adresáře a nazvi je(309 a 1090), pak je setřiď podle názvu a pochopíš můj problém.

Shrnutí:
1090 > 309, takže by mělo být pořadí (309, 1090), ale jak říkám řadí se to(1090, 309), protože 309 je 3 místný a 1090 je 4 místný, takže dojde k porovnaní(1 a 3) => špatné vyhodnocení Těchto dvou čísel.

 
Odpovědět 2.2.2014 11:45
Avatar
Drahomír Hanák
Tým ITnetwork
Avatar
Odpovídá na Milan Gallas
Drahomír Hanák:

V tom případě to není těžké opravit :)

['1090', '309'].map(Number);

// Případně
[{ price: '1090' }, { price: '309' }].forEach(function(elem) {
   elem.price = Number(elem.price);
});

Pokud ti takhle hodnoty vrací API (tj. nějaký serverový kód ať už to předáváš inline pomocí ngInit nebo nějakým Web API), měl by ceny předávat už jako čísla.

Taky je možné, že to dělá ngModel (pokud ho používáš). V tom případě zkus input[type="num­ber"]. Angular tak ví, že má hodnotu parsovat jako číslo. A nebo napiš vlastní direktivu, která přidá parser hodnoty z view:

angular.module('...').directive('parse-int', function() {
   return {
      restrict: 'A',
      require: 'ngModel',
      link: function (scope, element, attrs, ngModel) {
         ngModel.$parsers.push(function(value) { return Number(value); });
      }
   }
});

// HTML
<input ng-model="shop.items[0].price" parse-int />
Editováno 2.2.2014 12:16
 
Odpovědět  +1 2.2.2014 12:14
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 4 zpráv z 4.