Cały dzisiejszy wpis chciałbym poświęcić problemowi routingu w Angular JS. Na przykładzie naszego systemu (DEMO) widzimy, iż dobrze byłoby dodać możliwość wyświetlania podstron – osobnej strony dla postów i osobnej strony dla użytkowników. I właśnie tym zajmiemy się poniżej, korzystając z dobrodziejstwa modułu ngRoute.
Samo dodanie moduług ngRoute umożliwiającego tworzenie podstron i nawigację pomiędzy nimi nie gwarantuje sukcesu. Nadszedł czas, gdy musimy przemyśleć schemat naszej aplikacji, strukturę plików i modułów. Poniżej prezentuję schemat zaproponowanych zmian:
wraz z opisem przeznaczenia poszczególnych plików:
js/controllers/postsController.js | Obsługa widoku postów |
js/controllers/usersController.js | Obsługa widoku użytkowników |
js/services/api.js | Usługa łącząca system z API |
js/app.js | Skrypt agregujący wszystkie moduły systemu |
js/navigation.js | Usługa nawigacji (routingu) |
views/partials/posts.html | Widok postów |
views/partials/users.html | Widok użytkowników |
views/partials/main.html | Widok powitalny |
views/index.html | Główny widok aplikacji |
Jak widzimy nasza aplikacja przeszła małą metamorfozę. Zacznijmy od widoków:
index.html:
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 26 27 28 29 30 31 32 33 34 |
<html ng-app="ourApp"> <body> <style> .parzysty{ background-color:#CDCEED; } .nieparzysty{ background-color:#FBFBFF; } .wybrany{ background-color:#FD9F43; } </style> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.3/angular.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.7/angular-resource.js"></script> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.8/angular-route.js"></script> <script src="../js/services/api.js"></script> <script src="../js/navigation.js"></script> <script src="../js/app.js"></script> <script src="../js/controllers/usersController.js"></script> <script src="../js/controllers/postsController.js"></script> <a href="#!posts">Posts</a> <a href="#!users">Users</a> <div class="ng-view"></div> </body> </html> |
W naszym głównym widoku aplikacji dodaliśmy wszystkie skrypty JS, w znaczniku html dodaliśmy nasz główny moduł ourApp, (o którym więcej w pliku app.js). Widzimy również linki mające za zadanie prowadzić do kolejno postów i użytkowników.
Na samym końcu za pomocą znacznika div i klasy ng-view zdefiniowaliśmy miejsce, w którym mają być umieszczane zrenderowane wyniki pracy kontrolerów. Oznacza to, iż po wybraniu posts lub users system ma za pomocą odpowiedniego kontrolera i widoku częściowego utworzyć kod html, który zostanie osadzony właśnie w tej warstwie.
view/partials/posts.html
1 2 3 4 5 6 7 8 |
<div ng-controller="PostsController as vm"> <h1>Posts</h1> <ul > <li ng-repeat="x in vm.postsfromapiservice.items" ng-class-odd="'nieparzysty'" ng-class-even="'parzysty'"> {{ x.id}} | {{x.body}} </li> </ul> </div> |
view/partials/users.html
1 2 3 4 5 6 7 8 9 |
<div ng-controller="UsersController as vm"> <h1>Users</h1> <ul > <li ng-repeat="x in vm.usersfromapiservice.items" ng-class-odd="'nieparzysty'" ng-class-even="'parzysty'"> {{ x.id}} | {{x.email }} </li> </ul> </div> |
Oba widoki są trywialne, więc nie będę zagłębiał się w ich opis. Przejdźmy do kodu JS:
js/services/api.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 26 |
(function() { var dependencies = [ 'ngResource' ]; angular .module('api', dependencies) .factory('posts', ['$http','$resource', posts]) .factory('users', ['$http','$resource', users]); function posts($http,$resource) { var resource = $resource('https://jsonplaceholder.typicode.com/:object/:id', {}, { 'findall': {method: 'GET', params: {object: 'posts'},isArray:true}, 'find': {method: 'GET', params: {object: 'posts'}} }); return resource; } function users($http,$resource) { var resource = $resource('https://jsonplaceholder.typicode.com/:object/:id', {}, { 'findall': {method: 'GET', params: {object: 'users'},isArray:true}, 'find': {method: 'GET', params: {object: 'users'}} }); return resource; } })(); |
Jak widzimy na załączonym kodzie tworzymy moduł o nazwie api, który zawiera dwie usługi – posts i users. Jak się można domyślić, będą odpowiadały za pobranie postów i użytkowników – o samej usłudzie resource można poczytać w wcześniejszych wpisach.
Dwa kolejne skrypty JS to kontrolery:
js/controllers/postsController.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
angular.module('ourApp') .controller('PostsController', function($http,posts,$scope) { var vm = this; vm.posts = false; vm.postsfromapiservice = {}; vm.choosenPost = {}; posts.findall({}, function(data) { vm.postsfromapiservice.items = data; }); }); |
js/controllers/usersController.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
angular.module('ourApp') .controller('UsersController', function($http,users,$scope) { var vm = this; vm.users = false; vm.usersfromapiservice = {}; vm.choosenUser = {}; users.findall({}, function(data) { vm.usersfromapiservice.items = data; }); }); |
Jak widzimy, podstawową i jedyną funkcją obu kontrolerów jest pobranie z usługi api odpowiednich danych, po czym przekazanie ich do widoku.
Do omówienia pozostały nam dwa skrypty, zacznijmy od tego bardziej ogólnego:
js/app.js
1 2 3 4 5 6 7 8 |
(function() { angular.module('ourApp', [ 'api', 'ourApp.navigation' ]); })(); |
Główną funkcją naszego skryptu app.js jest agregacja wszystkich modułów systemu w moduł będący korzeniem aplikacji. W tym celu stworzyliśmy moduł ourApp i dodaliśmy dwa wykorzystane w aplikacji moduły:
– api
– ourApp.navigation
W ten sposób mamy korzeń naszej aplikacji, użyty bezpośrednio w głównym widoku index.html.
Na sam koniec zostawiłem najważniejszy skrypt dzisiejszego odcinka. W pliku navigation.js zdefiniujemy moduł nawigacji, a także podamy możliwe opcje routingu naszej aplikacji:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
angular.module('ourApp.navigation',['ngRoute']) .config(function($routeProvider,$locationProvider) { $routeProvider .when("/", { templateUrl : "../views/partials/main.html" }) .when("/posts", { templateUrl : "../views/partials/posts.html", controller : "PostsController" }) .when("/users", { templateUrl : "../views/partials/users.html", controller : "UsersController" }) }); |
Moduł nawigacji, przy użyciu ngRoute definiuje, co ma wydarzyć się w momencie gdy użytkownik wybierze konkretny adres, w przypadku domyślnego adresu ‘/’ nastąpi wywświetlenie pustego pliku main.html. W przypadku /posts zostanie użyty widok posts.html z kontrolerem PostsController.
Aplikacja użyje kontrolera PostsController, który wygeneruje treść wstrzykniętą do widoku posts.html, a przy tym całość zostanie umieszczona w pliku index.html w znaczniku <ng-view></ng-view>. Taki system szablonów znacznie ułatwia pracę.
Jak widać, niniejszy wpis miał w dużej części zająć się routingiem, a w praktyce jego większość opisuje strukturę aplikacji angularowej wraz z niezbędnymi zależnościami.