Menu, zakładki na stronie internetowej, linki do podstron. To coś jest tak naturalne, że nie wyobrażamy sobie budowania bez tego aplikacji internetowych.
W HTML robi się to bardzo prosto. Po prostu wstawimy linki <a>
do kolejnych plików podstrona1.html
, podstrona2.html
itd. Ma to jednak wady. Musimy kopiować całą zawartość strony (menu, stopka itd.), aby zmienić tylko jej środek (zawartość na którą chcemy się przełączyć). Jeśli zmienimy np. coś w menu, musimy ręcznie zaktualizować to na wszystkich podstronach. Dodatkowo musimy dbać np. o podświetlenie pozycji w menu w zależności od tego, na której zakładce jesteśmy.
W różnych językach programowania (np. PHP) możemy to zautomatyzować. We frameworkach do budowy aplikacji internetowych jest to różnie rozwiązane. Oczywiście w AngularJS również jest na to sposób i nazywa się routing. Na początku wygląda to trochę skomplikowanie, ale w późniejszej pracy jest bardzo wygodne. Dzięki temu można budować nawet proste strony z wykorzystaniem możliwości Angulara.
W Angularze dodatkową zaletą jest to, że podczas przełączania zakładek, nie będzie przeładowywana cała strona, a tylko odpowiedni jej fragment. Jest to typ aplikacji SPA (Single Page Application).
Integracja z Bootstrapem w AgendaEditor
Na końcu poprzedniego wpisu uzyskaliśmy już całkiem ładną aplikację dzięki podłączeniu Bootstrapa. Jak widzicie dodałem w Bootstrapie menu na górze, jednak jest ono nieaktywne. Zróbmy z niego użytek.
W aplikacji AgendaEditror chciałbym na początek takie zakładki:
- Home (zakładka z agendą i polami do wpisywania),
- Import (potem zaimplementujemy funkcjonalność, dzięki której będzie można w pole wklejać dane do agendy),
- About (informacje o aplikacji, kontakt itd.).
Routing w AngularJS i struktura aplikacji
Od wersji 1.2 w Angularze routing jest wydzielony do osobnego modułu ngRoute
w pliku angular-routing.js
, który możemy znaleźć na stronie projektu. Musimy go dołączyć do naszej aplikacji, jednak pamiętając o zachowaniu kolejności dodawania, która była już omówiona we wpisie, do którego linkuję.
1 |
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.3/angular-route.min.js"></script> |
Bez tego, po skonfigurowaniu routingu, otrzymalibyśmy błąd:
Error: $injector:modulerr
Module Error
Oczywiście należy w aplikacji dodany moduł zaincludować:
1 |
var app = angular.module('agendaEditor', ['ngRoute']); |
Kolejną rzeczą, którą musimy zrobić, to dodanie jakiegoś elementu (zazwyczaj <div>
) z dyrektywą ng-view
w pliku index.html. Angular w tym miejscu będzie podmieniał zawartość kolejnych podstron.
1 |
<div ng-view></div> |
Routing w Angularze działa w ten sposób, że podmienia element z dyrektywą ng-view
na plik szablonu (template), na podstawie adresu wpisanego w przeglądarce np. http://nazwaAplikacji/#/home
.
Potrzebujemy do każdej podstrony:
- plik z szablonem (html),
- plik z kontrolerem (js).
Należy skonfigurować $routeProvider
, w którym określimy jaki adres będzie odpowiadał jakiemu szablonowi i kontrolerowi.
Jak podzielić naszą aplikację na pliki? Jaka powinna być struktura projektu? Są dwa sposoby.
Sposób 1 – pogrupowane js i html
Tak jak już dawno robiło się w projektach: trzymajmy razem pliki js
w jednym folderze, a pliki html
w innym folderze:
- index.html
- css/
- style.css
- js/
- app.js
- HomeController.js
- ImportController.js
- AboutController.js
- pages/
- home.html
- import.html
- about.html
Każdemu kontrolerowi (np. HomeController.js
) odpowiada jeden plik szablonu (np. home.html
).
Możemy teraz dołączyć pliki kontrolerów do naszego index.html
:
1 2 3 4 |
<script src="js/app.js"></script> <script src="js/HomeController.js"></script> <script src="js/ImportController.js"></script> <script src="js/AboutController.js"></script> |
W każdym pliku umieszczamy kontroler według wzoru tak jak wcześniej (tu np. ImportController.js
):
1 2 3 |
app.controller('importController', function($scope) { // zawartość kontrolera... }); |
A w szablonach umieszczamy treść, która ma być podmieniana:
1 2 3 |
<div class="container"> Tu będzie zawartość importu. </div> |
Prawda, że wygląda to dobrze?
A teraz najważniejsza część: konfiguracja $routeProvider
(w pliku app.js
):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
app.config(function($routeProvider) { $routeProvider .when('/home', { templateUrl: 'pages/home.html', controller: 'homeController' }) .when('/import', { templateUrl: 'pages/import.html', controller: 'importController' }) .when('/about', { templateUrl: 'pages/about.html', controller: 'aboutController' }) .otherwise({ redirectTo: '/home', controller: 'homeController' }); }); |
Po prostu na obiekcie naszej aplikacji wykonujemy metodę config, która przyjmuje parametr z funkcją, w której jest konfiguracja. W konfiguracja dla każdej podstrony określamy:
when
– czyli jaki ciąg znaków będzie w adresie aplikacji po znaku hash np./#/home
.templateUrl
– gdzie jest plik z szablonem, który ma być wstawiony na stronę w miejsce ng-view.controller
– jak nazywa się kontroler, który ma obsługiwać daną podstronę (będzie dynamicznie podmieniany). Jest to pole opcjonalne (nie na każdej podstronie potrzebujemy kontrolera).
Na koniec możemy dodać jeszcze otherwise
(i reszta konfiguracji jak w przypadku when
), który określa co ma się stać (na którą podstronę wejść) w przypadku jak nie można było dopasować do żadnej reguły wymienionej we when
.
Cały kod wraz ze strukturą możecie podejrzeć na Githubie: https://github.com/mkczyk/agenda-editor/tree/routing-grouped-types-angularjs
Podgląd działającej aplikacji sposobem 1:
Sposób 2 – osobny folder dla każdej podstrony
Drugi sposób na początku trudniej zrozumieć (bo konfiguracja $routeProvider
jest porozrzucana po wielu plikach), ale potem dużo łatwiej z nim pracować. Każda podstrona na swój folder, w którym znajduje się plik szablonu i kontrolera. W kontrolerze jest umieszczona konfiguracja $routeProvider
.
Jeśli w przyszłości będziemy chcieli dodać kolejną podstronę, to wystarczy skopiować jeden folder, przerobić pliki (z konfiguracją), dodać w index.html pozycję w menu i już mamy gotowe.
- index.html
- app.js
- css/
- style.css
- home/
- home.html
- home.js
- import/
- import.html
- import.js
- about/
- about.html
- about.js
Dołączmy pliki do naszego index.html
:
1 2 3 4 |
<script src="app.js"></script> <script src="home/home.js"></script> <script src="import/import.js"></script> <script src="about/about.js"></script> |
W głównym pliku konfiguracyjnym app.js możemy umieścić tylko to co nie pasuje do żadnej podstrony czyli otherwise
:
1 2 3 4 5 6 7 |
app.config(function($routeProvider) { $routeProvider .otherwise({ redirectTo: '/home', controller: 'homeController' }); }); |
Szablony HTML będą wyglądały identycznie jak w poprzednim sposobie. A cała potęga tego sposobu tkwi w konfiguracji danej podstrony (tu import.js
):
1 2 3 4 5 6 7 8 9 10 11 12 13 |
angular.module('agendaEditor') .config(function($routeProvider) { $routeProvider .when('/import', { templateUrl: 'import/import.html', controller: 'importController' }) }) .controller('importController', function($scope) { // zawartość kontrolera... }); |
W środku konfiguracja wygląda tak samo jak w poprzednim sposobie. Tylko, że tu dotyczy jedynie danej podstrony.
Dodatkowa uwaga: jeśli kontroler nam się mocno rozrośnie, możemy konfigurację podzielić na dwa pliki (plik z kontrolerem i plik z konfiguracją $routeProvider
). Tylko trzeba pamiętać, żeby w index.html dodawać po dwa pliki.
Cały kod tego sposobu wraz ze strukturą możecie podejrzeć na Githubie: https://github.com/mkczyk/agenda-editor/tree/routing-grouped-subpages-angularjs
Podgląd działającej aplikacji sposobem 2:
Pingback: Uruchamianie aplikacji AngularJS | Marcin Kowalczyk – Blog IT
Bardzo fajnie wygląda ten projekt 🙂 Czy myślałeś aby klasycznego podejścia do kontrolerów nie użyć składni „controllerAs” i pominąć scopea?
Pingback: AgendaEditor: wymiana danych pomiędzy zakładkami poprzez serwisy | Marcin Kowalczyk – Blog IT
Pingback: Relacja z Warszawskich Dni Informatyki 2018 oczami Java Developera – Marcin Kowalczyk – Blog IT