Routing i Widoki

Routing i widoki

 

W poniższym rozdziale omówimy tworzenie layoutu dla aplikacji które wykorzystują kilka widoków, które wyświetlają się w zależności od paska adresu url w przeglądarce. Służy do tego specjalny moduł w AngularJS który nazwa się ngRoute.

Nasza aplikacja powoli rośnie i staje się bardziej złożona. W poprzednich przypadkach zwracała jeden konkretny widok, a cały kod szablonu znajdował się w pliku lista-telefonów.template.html. Kolejnym krokiem w budowaniu aplikacji to dodanie do niej widoku, który wyświetli szczegółowe informacje o każdym z urządzeń na naszej liście.

Aby dodać widok szczegółowy, wrócimy do pliku index.html, następnie dostosowując go w taki sposób by stał się głównym szablonem naszego układu. Będzie on wspólny dla wszystkich widoków w aplikacji. Inne częściowe widoki będą dostępne w tym układzie na podstawie obecnego route’a, czyli kodu który aktualnie jest wyświetlany użytkownikowi.

Deklarowanie route’ów w AngularJS’ie oparte jest na module o nazwie $routeProvider, który jest dostawcą usługi $route. Ta usługa ułatwia łączenie kontrolerów, wyświetlanie szablonów i bieżącej lokalizacji adresu URL w przeglądarce. Korzystając z tej funkcji, możemy wdrożyć precyzyjne łączenie, które pozwala nam wykorzystywać historię przeglądarki (nawigacja wstecz i do przodu) oraz zakładki.

Ponieważ już się dowiedzieliśmy, że komponenty pozwalają nam łączyć kontrolery z szablonami w sposób modułowy, będziemy także używać komponentów do routingu. Każdy route będzie powiązany z komponentem, a ten komponent będzie odpowiedzialny za udostępnienie szablonu widoku i kontrolera.

Trochę o DI, Injector i Providers.

Silnik Angular.JS oparty jest na DI (Dependency Injection). Dlatego ważne jest by zrozumieć o co w nim chodzi.

Gdy aplikacja załaduje się, framework tworzy injector (czyli element wstrzykujący), który będzie używany do znalezienia i wstrzyknięcia usług, które są wymagane przez aplikację. Sam injector nie wie nic o tym, co robią usługi $http lub $route. W rzeczywistości inejctor nawet nie wie o istnieniu tych usług, chyba że jest skonfigurowany z odpowiednimi definicjami modułów.

Injector wykonuje tylko następujące czynności:

 

  • załadowuje definicje modułów określone w aplikacji

  • rejestruje wszystkich dostawców zdefiniowanych w tych definicjach

  • gdy zostanie o to poproszony, zainicjuje usługi i ich zależności za pośrednictwem dostawców, jako parametr dla funkcji wstrzykiwania.

Providers to obiekty które zapewniają (tworzą) wystąpienia usług i udostępniają interfejsy API konfiguracji, które mogą być używane do kontrolowania zachowania kreacji i środowiska wykonawczego usługi. W przypadku usługi $route, $routeProvider udostępnia API, które pozwala ci definiować routy dla Twojej aplikacji.

Moduły AngularJS rozwiązują problem usuwania globalnych zmiennych z aplikacji i zapewniają sposób konfiguracji injectora. W przeciwieństwie do modułów AMD lub require.js, nie próbują rozwiązać problemu ładowaniem skryptów.

 

Konfiguracja po stronie szablonu

Pierwszą rzeczą o którą należy zadbać to dołączenie zewnętrznego modułu który odpowiada za routing. Można go pobrać z następującej strony:

 

https://github.com/angular/bower-angular-route

 

Następnie dodać po stronie index.html ścieżkę w znaczniku script.

 

<script src="angular-route/angular-route.js"></script>

 

Dzięki temu nasz moduł jest już dostępny do wykorzystania po stronie AngularJS’a.

Należy teraz umieścić dyrektywę ng-view do elementu w którym chcemy by był wstrzykiwany poszczególny kod dla html.

 

Konfiguracja po stronie modułu

Po pierwsze, należy podpiąć router do modułu głównego z naszą aplikacją.

 

angular.module('app', ['ngRoute']);

 

Teraz musimy stworzyć konfigurację odpowiednich routów. Wykorzystamy do tego oddzielny plik:

 

app/app.config.js

angular.module('app').config(['$locationProvider', '$routeProvider', function ($locationProvider, $routeProvider) {
$locationProvider.hashPrefix('!');

$routeProvider.when('/telefony', {
template: '<telefony-lista></telefony-lista>'

})

.when('/telefony/:telefonId', {

template: '<telefon-szczegoly></telefon-szczegoly>'

})

.otherwise('/telefony');
}]);

 

Korzystając z metody config(), żądamy aby niezbędne providery (np. routeProvider) zostały wstrzyknięte do naszej funkcji konfiguracyjnej, dzięki temu jesteśmy w stanie użyć ich metod do określenia zachowania odpowiednich usług. Tutaj używamy metod $routeProvider.when() i $routeProvider.otherwise() aby zdefiniować routy aplikacji.

 

Routy są zdefiniowane za pomocą:

 

  • when('/telefony') - określa widok, który będzie wyświetlany gdy fragment URL będzie zawierał frazę '/telefony'. Zgodnie z określonym szablonem AngularJS utworzy instancję komponentu telefonyLista do zarządzania widokiem.

  • when('/telefony/:telefonId') - określa widok, który zostanie wyświetlony, gdy fragment adresu URL pasuje do /telefony/<telefonId>, gdzie <telefonId> jest zmienną częścią adresu URL. Odpowiedzialny za widok będzie komponent TelefonSzczegoly.

  • otherwise('/telefony') - przekierowuje na adres /telefony gdy URL nie zostanie dopasowany do wcześniejszych konfiguracji.

Ponownie użyliśmy komponentu telefonyLista, który już zbudowaliśmy i nowego "fikcyjnego" komponentu telefonSzczegoly. Na razie komponent telefonSzczegoly wyświetli tylko identyfikator wybranego telefonu.

Zwróć uwagę na użycie parametru: telefonId w drugiej deklaracji route’a. Usługa $route używa deklaracji trasy - '/telefony/:telefonId' - jako szablonu dopasowanego do bieżącego adresu URL. Wszystkie zmienne zdefiniowane za pomocą prefiksu „:” są wyodrębniane do obiektu $routeParams.

 

Komponent telefonSzczegoly

Stworzyliśmy komponent telefonSzczegoly do obsługi widoku szczegółów telefonu. Postępowaliśmy zgodnie z tymi samymi konwencjami, co w przypadku listy telefonicznej, używając osobnego katalogu i tworząc moduł telefonSzczegoly, który dodaliśmy jako zależność modułu App.

 

telefon-szczegoly.module.js

angular.module('telefonSzczegoly', ['ngRoute']);

 

telefon-szczegoly.component.js

angular

.module('telefonSzczegoly')

.component('telefonSzczegoly', {

template: 'Szczegoly dla telefonu o id {{$ctrl.telI}}'

})

.controller: ['$routeParams', function ($routeParams) {

this.telId = $routeParams.telefonId

}];

 

app.module.js

angular.module('app', [

'telefonSzczegoly'

])

 

Moduł telefon zależy od modułu ngRoute, udostępnia on obiekt $routeParams, który jest używany w kontrolerze komponentu telefonSzczegoly. Ponieważ ngRoute jest także zależny od głównego modułu App, jego usługi i dyrektywy są już dostępne wszędzie w aplikacji (w tym składnik telefonSzczegoly).

Oznacza to, że nasza aplikacja będzie działała, nawet jeśli nie uwzględnimy ngRoute na liście zależności dla komponentu telefonSzczegoly. Chociaż może być kuszące pominięcie zależności podmodułu, który jest już importowany przez moduł główny, łamie on zasady modułowości (ponieważ bez ngRoute moduł telefonSzczegoly nie będzie poprawnie działał).

 

Przykład aplikacji z routingiem

 

Zbierzmy zatem wszystkie elementy poznane do tej pory i rozbudujmy naszą aplikację o kolejny element który nazywa się Service. Jest to kod który najczęściej służy do przetrzymywania danych lub wyciągania ich z backendowego API. Dzięki zastosowaniu serwisu, możemy wielokrotnie używać go w aplikacji. Zatem, obecna struktura aplikacji wygląda w następujący sposób:

 

app/

index.html

app.module.js

app.config.js

telefony.service.js

telefon-lista/

telefon-lista.component.js

telefon-lista.template.html

telefon-lista.module.js

telefon-szczegoly/

telefon-szczegoly.component.js

telefon-szczegoly.template.html

telefon-szczegoly.module.js

 

Nowym plikiem który powstał jest telefony.service.js. Jego kod wygląda w następujący sposób:

Dzięki temu możemy wyciągać dane bezpośrednio z serwisu, odwołując się do jego nazwy. Aktualny kod w index.html. Ważna jest również kolejność plików które ładujemy z AngularJS’a.

Zaprezentujmy teraz wygląd komponentów, została w nich zaprezentowana komunikacja z serwisem:

telefon-lista.component.js

telefon-szczegoly.component.js

Dzięki temu w kontrolerach poszczególnych komponentów możemy operować na tych samych danych. Dodatkowo dobrą praktyką jest umieszczanie w serwisie zapytań ajaxowych i na podstawie metod, wyciągać dane wyniku w kontrolerze.

Gotowy kod aplikacji jest gotowy do pobrania pod adresem:

http://jsystems.pl/static/angularjs/angularjs.zip

Należy również pamiętać o tym, by uruchomić projekt najlepiej byłoby go umieścić na lokalnym serwerze lub wyłączając funkcjonalność przeglądarki dotyczącej błędu Cross-site Scripting.

Po uruchomieniu w przeglądarce aplikacja powinna mieć następujący widok:

 

Elementy dostępne w AngularJS