Одним из ключевых компонентов всех MVC-фреймворков являются представления (view). И фреймворк AngularJS не исключение, с тем только отличием, что AngularJS позволяет при использовании представлений создавать одностраничные приложения или так называемые Single Page Applications. Посмотрим на примере.
Для этого содадим новый проект. Для файлов javascript в проект добавим для папку js. В этой папке создадим новый каталог lib, в который положим два файла фреймворка AngularJS: angular.min.js и angular-route.min.js.
Файл angular-route.min.js идет вместе с главной библиотекой AngularJS и он как раз содержит функциональность модуля
ngRoute
, который необходим для обеспечения маршрутизации.
Теперь укажем маршруты для использования представлений. Добавим в папку js файл определения модулей приложения app.js:
var questApp = angular.module('questApp', ["ngRoute"]) .config(function($routeProvider){ $routeProvider.when('/question', { templateUrl:'views/question.html', controller:'QuestionController' }); $routeProvider.when('/answer', { templateUrl:'views/answer.html', controller:'AnswerController' }); });
Чтобы подключить функциональность маршрутов, нам надо подключить другой модуль ngRoute
.
С помощью метода config
мы определяем маршруты приложения. Для конфигурации маршрутов используется объект $routeProvider.
Метод $routeProvider.when
принимает два параметра: название маршрута и объект маршрута. Объект маршрута задает представление и
обрабатывающий его контроллер с помощью параметров templateUrl и controller. Поэтому для представлений нам не надо определять контроллер с помощью директивы.
Итак, у нас есть два маршурта, и для каждого маршрута нам надо создать свой контроллер и свое представление. Поэтому в папке js также создадим еще один каталог controllers. В него добавим новый файл QuestionController.js:
questApp.controller('QuestionController', function QuestionController($scope, dataService){ var promiseObj=dataService.getData(); promiseObj.then(function(value) { $scope.question=value; }); $scope.voteUp = function (answer){ answer.rate++; }; $scope.voteDown = function (answer){ answer.rate--; }; } )
И также добавим в папку js/controllers еще один файл AnswerController.js:
questApp.controller('AnswerController', function AnswerController($scope, $http){ $scope.response={}; $scope.save = function (answer, answerForm){ if(answerForm.$valid){ $http.post("postAnswer.php", answer).then(function success(response) { $scope.response=response.data; }); } }; } )
Первый контроллер QuestionController использует для получения даных сервис dataService, поэтому создадим его. Для этого в папку js добавим новый файл dataService.js:
questApp.factory('dataService', function($http, $q){ return{ getData: function(){ var deferred = $q.defer(); $http({method: 'GET', url: 'question.json'}). then (function success(response) { deferred.resolve(response.data.question); }, function error(response) { deferred.reject(response.status); } ); return deferred.promise; } } })
Этот сервис обращается к файлу question.js, который находится в корневом каталоге:
{ "question":{ "text": "Какой js-фреймворк лучше использовать?", "author": "Иван Иванов", "date": "20/10/2013", "answers": [{ "id" : 1, "text": "AngularJS!", "author": "Вова Сидоров", "date": "20/10/2013", "rate":2 },{ "id" : 2, "text": "AngularJS лучше всех", "author": "Олег Кузнецов", "date": "21/10/2013", "rate":3 }] } }
И также в корневом каталоге для представлений содадим папку views. Поместим в папку views первое представление, которое назовем question.html и которое будет иметь следующий код:
<div> <div class="quest"> <h3>{{question.text}}</h3> <p>{{question.author}} </p> <p><i>{{question.date}}</i></p> </div> <h3>Ответы</h3> <div class="answers"> <div ng-repeat="answer in question.answers | orderBy:sortparam" class="answer"> <div class="vote"> <a class="vote-up" ng-click="voteUp(answer)"></a> <span class="rate">{{answer.rate}}</span> <a class="vote-down" ng-click="voteDown(answer)"></a> </div> <b>{{answer.text}}</b> <p>{{answer.author}}</p> <p><i>{{answer.date}}</i></p> </div> </div>
Это представление будет выводить вопрос и список ответов на него, взаимодействуя с контроллером QuestionController
. То есть
для представления не нужны теги body и head, только разметка, имеющая отношение к самому представлению. Обратите внимание, что элемент div
верхнего уровня не содержит директивы ng-controller="QuestionController"
, как в предыдущих темах. Так как связь с контроллером мы
зададим напрямую с помощью определения маршрута.
Второе представление пусть будет содержать форму для ответа. Назовем его answer.html и добавим в него следующий код:
<div ng-controller="QuestionController"> <div class="quest"> <h3>{{question.text}}</h3> <p>{{question.author}} </p> <p><i>{{question.date}}</i></p> </div> </div> <div> <form name="answerForm"> <fieldset> <p><label for="answerText">Текст ответа</label> <input id="answerText" ng-model="answer.text" required placeholder="Введите ответ" /></p> <p><label for="answerAuthor">Автор ответа</label> <input id="answerAuthor" ng-model="answer.author" required placeholder="Введите автора" /></p> </fieldset> <button type="submit" ng-click="save(answer, answerForm)">Сохранить</button> </form> </div>
Здесь немного иначе. Так как мы можем при определении маршрута указать для представления только один контроллер для представления, здесь явным
образом с помощью директивы ng-controller="QuestionController"
для первого блока div указано, что этот блок будет обрабатываться
другим конкретным контроллером.
И в конце определим в корне проекта главную веб-страницу index.html:
<!doctype html> <html ng-app="questApp"> <head> <meta charset="utf-8"> <link rel="stylesheet" href="css/mystyles.css" /> </head> <body> <nav><a href="#!question">Вопрос</a>|<a href="#!answer">Ответить</a></nav> <ng-view></ng-view> <script src="js/lib/angular.min.js"></script> <script src="js/lib/angular-route.min.js"></script> <script src="js/app.js"></script> <script src="js/dataService.js"></script> <script src="js/controllers/QuestionController.js"></script> <script src="js/controllers/AnswerController.js"></script> </body> </html>
В итоге проект будет иметь следующую структуру:
Для перемещения по представлениям здесь использовано навигационное меню nav
с ссылками типа <a href="#/question">Вопрос</a>
.
Атрибут href
здесь не указывает на конкретную страницу html. Он указывает на маршрут, определенный ранее. Перед каждым маршрутом стоит знак # для
того, чтобы в браузере можно было при необходимости обращаться напрямую к данному ресурсу.
После определения меню идет директива <ng-view></ng-view>
, определенная в
модуле ngRoute. Она представляет место, где производиться рендеринг представления, соответствующего маршруту.
Теперь, если мы запустим приложение, то нам отобразиться примитивное меню. Выберем из него первый пункт, и сразу приложение выведет на странице index.html представление views/question.html:
Теперь выберем другой пункт меню, и сработает другой маршрут, хотя мы по-прежнему будем находиться на странице index.html:
Хотя все у нас работает, но при первом обращении к приложению у нас не используется никакого представления, и мы наблюдаем только меню. Но мы можем это исправить, указав маршрут по умолчанию. Для этого изменим файл конфигурации модуля приложения следующим образом:
var questApp = angular.module('questApp', ["ngRoute"]) .config(function($routeProvider){ $routeProvider.when('/question', { templateUrl:'views/question.html', controller:'QuestionController' }); $routeProvider.when('/answer', { templateUrl:'views/answer.html', controller:'AnswerController' }); $routeProvider.otherwise({redirectTo: '/question'}); });
Метод $routeProvider.otherwise
указывает на маршрут по умолчанию. В данном случае будет загружаться представление views/question.html.
Таким образом, фреймворк AngularJS позволяет нам легко создавать одностраничные приложения.