Dostawca stanu i dyrektywa ui-view

Zanim pojawił się AngularJS aplikacje webowe budowaliśmy najczęściej według jednego schematu. Tworzyliśmy stronę dostępną pod konkretnym adresem URL, na której asynchronicznie (ajaxem) obsługiwaliśmy akcje pobierania i odświeżania danych. Każda taka akcja polegała na wysłaniu żądania HTTP oraz oczekiwaniu na odpowiedź z danymi. Po otrzymaniu odpowiedzi aktualizowaliśmy widok strony bez jej przeładowania. Aktualizacja widoku polegała na podmianie komponentu na nowy, najczęściej za pomocą własności (lub funkcji) innerHTML.

Dużym ograniczeniem tego rozwiązania było to, że tak naprawdę mieliśmy do dyspozycji jeden podstawowy widok, w którym wymienialiśmy jedynie poszczególne klocki. Natomiast wiadomo, że aplikacja webowa wymaga często wielu różnych widoków reprezentowanych przez całe strony, a nie tylko ich części. W takiej sytuacji mieliśmy zwykle dwa wyjścia. Mogliśmy albo tworzyć osobne strony, które były pobierane osobnymi requestami i leżały pod różnymi adresami na serwerze (mało wydajne), albo korzystać z pojawiających się bibliotek obsługujących routing po stronie samego Javascriptu, co wymagało jednak dodatkowej wiedzy i zaangażowania.

Stany w aplikacji AngularJS

W momencie gdy pojawił się AngularJS szybko popularne stało się pojęcie stanu aplikacji. Definiowanie stanów z różnymi widokami stworzyło nam możliwość zarządzania stroną w ramach jednego adresu URL na serwerze, ale pod różnymi URL-ami widzianymi w przeglądarce przez użytkownika.

Przykładowo jeśli nasza aplikacja StartAPPa jest dostępna pod adresem:

www.javappa.com/aplikacja

to pomimo, że przełączając się pomiędzy kolejnymi widokami widzimy regularnie zmieniający się URL w przeglądarce:

www.javappa.com/application/appa/admin
www.javappa.com/application/appa/create
www.javappa.com/application/appa/items,

to i tak cały czas znajdujemy się na stronie załadowanej RAZ, na samym początku z adresu:

www.javappa.com/aplikacja

Przechodząc pomiędzy widokami mamy wrażenie jakbyśmy za każdym razem zmieniali stronę (ze względu na zmieniający się URL), mimo że tak naprawdę nie pobieramy nowych stron z serwera i cały czas pozostajemy na tej jednej załadowanej na starcie. To właśnie strona pod tym adresem jest odpowiedzialna za trzymanie odpowiedniej struktury HTML oraz załadowanie skryptów JS i plików ze stylami. Każde przejście użykownika do innego widoku inicjuje jedynie asynchroniczne pobranie danych potrzebnych dla tego widoku, jak również dostarcza wymagane komponenty do przechowywania/prezentacji tych danych. Dzięki temu, że nie pobieramy wtedy z serwera skryptów JS i plików css, jak również nie musimy pobierać całego szablonu strony. Przechodzenie między kolejnymi widokami staje się bardzo szybkie i sprawia wrażenie jakby nie wymagało w ogóle połączenia internetowego (oczywiście o ile nie pobieramy bardzo dużych zbiorów danych).

Dostawca stanu ($stateProvider)

Tak jak pisaliśmy wcześniej, każdy z widoków wraz z przypisanym do niego URL-em jest nazywany stanem. Jeśli chcemy stworzyć aplikację z wieloma stanami potrzebujemy dodatkowo wprowadzić odpowiedni mechanizm zarządzający. Takim właśnie mechanizmem jest $stateProvider.

Należy jednak pamiętać, że $stateProvider nie jest providerem wbudowanym w AngularJS. Aby z niego korzystać musimy ściągnąć i podpiąć do naszej aplikacji dodatkowy moduł zewnętrzny ui-router:
angular.module('Appa', [ 'ui.router', ... ])
Następnie w sekcji config aplikacji wstrzykujemy naszego dostawcę stanu wraz z dostawcą routingu URL $urlRouterProvider. Od teraz możemy przystąpić już do definiowania stanów aplikacji:
angular.module('Appa').config([ '$stateProvider', '$urlRouterProvider', 
                                        function($stateProvider, $urlRouterProvider) {
                            
	$stateProvider.state('home', {
		url : '/',
		templateUrl : 'appalogin/home/home.html',
		controller : 'HomeCtrl'
	}).state('login', {
		url : '/login',
		templateUrl : 'appalogin/appa/appa.login.html',
		controller : 'AppaCtrl'
	}).state('auth', {
		url : '/auth',
		templateUrl : 'appalogin/auth/auth-login.html',
		controller : 'AuthCtrl',
	}).state('resetPasswordForm', {
		url : '/resetPasswordForm',
		templateUrl : 'appalogin/auth/auth-login-reset.html',
		controller : 'AuthCtrl',
	}) ...
    
	$urlRouterProvider.otherwise('/');

} ]);
Widzimy, że dla każdego ze stanów określone zostały parametry:
  • url - URL wstawiany w przeglądarce, w momencie gdy nakazujemy frameworkowi przekierowanie do danego stanu
  • templateUrl - ścieżka do pliku, który zawiera kod HTML naszego widoku
  • controller - kontroler obsługujący widok (obsługuje zdarzenia, ustawia i pobiera dane z widoku itp.)

Przejście do stanu

No dobrze. Mamy zatem stany, a także określone powiązania do konkretnych widoków. Pojawia się teraz pytanie, w jaki sposób możemy przekierować użytkownika do danego widoku, gdy użytkownik ten kliknie w wybrany link na stronie? Otóż, możemy to wykonać na kilka sposobów:
  • Funkcja - $state.go([[NAZWA_STANU]])

    Jeśli mamy do czynienia z pojedynczym linkiem bądź przyciskiem, wówczas używamy dyrektywy ng-click:
    <button value="Login" class="btn btn-primary navbar-btn" ng-click="logout()">Logout</button>
    
    oraz zwyczajnie definiujemy w kontrolerze kod, który uruchamia odpowiednią funkcję: $state.go([[NAZWA_STANU]]):
    function logout() {
        ...
        
        $state.go('auth');
    
        ...
    }
    
    Oczywiście $state musi być najpierw wstrzyknięty do naszego kontrolera.
  • Dyrektywa ui-sref

    W przypadku gdy mamy w aplikacji grupę linków, na przykład w postaci menu, możemy też użyć dyrektywy ui-sref. Za przykład niech posłuży nam fragment kodu tworzący menu aplikacji StartAPPa. Przedstawia on trzy linki, które wiążemy ze stanami (admin, create, items), podając ich nazwy w dyrektywie ui-sref:
    <a ui-sref="admin" class="font-family-package selection-handler">
        My Account <span class="help-block">Formularz Podstawowy</span>
    </a> 
    <a ui-sref="create" class="font-family-package selection-handler">
        Create/Update Item <span class="help-block">Formularz Zaawansowany</span>
    </a>
    <a ui-sref="items" class="font-family-package selection-handler">
        New Items <span class="help-block">Tabela Podstawowa</span>
    </a>
    
    Nie musimy wywoływać żadnych dodatkowych funkcji z poziomu kontrolera. Dyrektywa ta sprawdza się doskonale gdy linki służą nam tylko do przełączania widoków, bez dokonywania dodatkowych operacji (np. pobierania danych).

Dyrektywa ui-view

W tym miejscu od razu trzeba wspomnieć, że dyrektywa ui-view jest po części kontynuacją poprzedniego paragrafu, gdyż podobnie jak ui-sref, ona też pozwala na podanie nazwy widoku, który chcemy pokazać. Podstawowa różnica polega na tym, że dyrektywa ta nie przeznaczona do użytku z linkami czy przyciskami, tylko działa w momencie załadowania kodu strony. Dyrektywę podajemy jako atrybut tagu div:
<div ui-view></div>
Dyrektywa umożliwia podanie również nazwy widoku, co pozwala na wprowadzenie widoku domyślnego:
<div ui-view="viewName"></div>
Jednak najważniejsze jest to, że gdy przełączamy się między widokami aplikacji przy pomocy $state.go lub ui-sref, to widok uzyskiwany ze $stateProvidera będzie wstawiony w kodzie HTML zawsze w miejscu położenia diva z tą dyrektywą:
<html ng-app="Appa">
<head>
<meta charset="utf-8">
<title>Appa</title>

...css...

</head>

<body ng-controller="AppCtrl">
    <div ui-view></div>
    
...js...
    
</body>
</html> 
Rekomendacja
Wszystkie opisane powyżej elementy stanowią niezbędną wiedzę do "sterowania ruchem" w tworzonej przez nas aplikacji. Nie wyczerpaliśmy jednak tego tematu do końca, więc zapraszamy również do sekcji linków dostępnych na dole strony. Znajduje się tam odnośnik do githuba projektu ui-router, gdzie znajdziecie kilka dodatkowych informacji. Z ciekawszych zagadnień warto zwrócić uwagę na opis przedstawiający sposób przekazywania parametrów do stanu.

Używamy w StartAPPa


Zgodnie z tym co już wielokrotnie wspominaliśmy, każdy moduł aplikacji StartAPPa jest przekształcany specjalnie dla Was w niezależnie działającą aplikację. Stąd też ściągnięty projekt zawiera charakterystyczną strukturę plików, zgodną z opisem z rozdziału: Projekt startowy Spring Boot + AngularJS. Po pobraniu projektu znajdziecie tam więc między innymi plik router.js, a w nim konfigurację stanów dostarczoną przez $stateProvidera.

Większość modułów, ze względu na swoją prostą konstrukcję posiada niewielką liczebność obsługiwanych stanów. "Najbogatszy" w tym temacie jest projekt w kursie Login & Reset, którego fragment przedstawiliśmy w bieżącym rozdziale (opisując $stateProvidera). Ze względu na swoją specyfikę (wykonywanie przekierowań do widoków logowania, resetowania hasła, ustawienia nowego hasła itp.) definiuje on i zarządza on aż siedmioma różnymi stanami.
Linki:
https://ui-router.github.io/ng1/docs/latest/modules/directives.html

Stale się rozwijamy, a więc bądź na bieżąco!
Na ten adres będziemy przesyłać informacje o ważniejszych aktualizacjach, a także o nowych materiałach pojawiających się na stronie.
Polub nas na Facebooku:
Nasi partnerzy: stackshare
Javappa to również profesjonalne usługi programistyczne oparte o technologie JAVA. Jeśli chesz nawiązać z nami kontakt w celu uzyskania doradztwa bądź stworzenia aplikacji webowej powinieneś poznać nasze doświadczenia.
Kliknij O nas .


Pozycjonowanie stron: Grupa TENSE