AngularJS является одной из самых популярных платформ JavaScript MV * и широко используется для создания одностраничных приложений (SPA). Одна из сложных функций в SPA — маршрутизация. Маршрутизация на стороне клиента включает в себя изменение части представления и создание записи в истории навигации браузера. В качестве полнофункциональной клиентской среды AngularJS всегда поддерживала маршрутизацию через модуль ngRoute
. Хотя этого достаточно для базовых маршрутов, он не поддерживает более сложные сценарии, такие как вложенные представления, параллельные представления или последовательность представлений.
В настоящее время разрабатывается новый маршрутизатор для Angular 2, и он будет перенесен на Angular 1.4. В этой статье мы увидим, как новый маршрутизатор можно использовать для определения маршрутов и как он решает некоторые проблемы, которые ngRoute
не мог.
Как уже говорилось, на момент написания этой статьи работа над новым маршрутизатором все еще продолжается, и некоторые из API могут позже измениться. Команда Angular еще не додумалась до названия нового маршрутизатора, поэтому сейчас он называется футуристическим маршрутизатором.
Ограничения ngRoute
ngRoute
не был создан с учетом сложных корпоративных приложений. Я лично видел приложения, в которых определенные части страницы должны быть загружены в несколько этапов. Такие приложения могут быть созданы с использованием ngRoute
, но практически невозможно иметь состояние URL для каждого изменения, примененного к представлению.
Директива ng-view
может использоваться только один раз внутри экземпляра директивы ng-app
. Это мешает нам создавать параллельные маршруты, поскольку мы не можем одновременно загружать два параллельных представления.
Шаблон представления, отображаемый внутри ng-view
не может содержать другую директиву ng-view
. Это мешает нам создавать вложенные представления.
Новый маршрутизатор решает эти проблемы и предоставляет гибкий способ определения и использования маршрутов. Новый маршрутизатор также использует Controller as
синтаксис . Я настоятельно рекомендую использовать Controller as
синтаксиса, так как это одно из соглашений, которым необходимо следовать сегодня, чтобы подготовиться к Angular 2.
Создание простых маршрутов
Новый маршрутизатор создается с учетом Angular 2. Angular 2 упростит внедрение зависимостей, исключив этап настройки модуля, что означает, что нам не нужно писать блок конфигурации для определения маршрутов — мы можем определить их где угодно.
Каждый маршрут, который будет добавлен к новому маршрутизатору, состоит из двух частей:
-
path
: URL шаблона маршрута -
component
: комбинация шаблона и контроллера. По соглашению, контроллер и шаблон должны быть названы в честь компонента
Маршруты настраиваются с помощью службы $router
. Поскольку $router
является службой, мы можем определить маршруты в любом месте приложения (кроме провайдера или блока конфигурации). Однако нам нужно убедиться, что блок кода, определяющий маршруты, выполняется сразу после загрузки приложения. Например, если маршруты определены в контроллере (как мы скоро это сделаем), контроллер должен выполняться при загрузке страницы. Если они определены в сервисе, метод сервиса должен выполняться в блоке выполнения.
Навигация между шаблонами
Давайте определим два простых маршрута и перейдем между ними, используя новый маршрутизатор. Если вы хотите следовать этому коду, вам необходимо получить копию нового маршрутизатора. Покажи мне как .
Вы можете установить новый маршрутизатор для каждого проекта через npm .
mkdir new-router && cd new-router npm install angular-new-router
Это создаст папку с именем node_modules
в каталоге вашего проекта. Новый маршрутизатор можно найти по адресу node_modules/angular-new-router/dist/router.es5.min.js
. Включите его в свой проект после самого AngularJS.
Прежде всего, давайте определим модуль и настроим маршруты:
angular.module('simpleRouterDemo', ['ngNewRouter']) .controller('RouteController', ['$router', function($router){ $router.config([ { path:'/', redirectTo:'/first' }, { path:'/first', component:'first' }, { path:'/second/:name', component:'second' } ]); this.name='visitor'; }])
Контроллер в приведенном выше фрагменте определяет три маршрута. Обратите внимание, что корневой маршрут перенаправляет на наш первый шаблон и что третий маршрут принимает параметр в URL. Как видите, синтаксис задания параметра такой же, как и для ngRoute
.
Как уже упоминалось, каждому компоненту требуется соответствующий шаблон представления и контроллер. По соглашению, имя контроллера должно быть именем компонента с суффиксом «Controller» (в нашем случае firstController
и secondController
). Имя шаблона представления должно совпадать с именем компонента. Он также должен находиться в папке с тем же именем, что и компонент, внутри папки с именем components
. Это даст нам:
projectRoot/ components/ first/ first.html second/ second.html
Эти соглашения могут быть переопределены с помощью $componentLoaderProvider
. Мы увидим пример этого позже, а пока давайте придерживаться соглашений.
Далее идут виды для компонентов, которые были использованы в first
и second
. Мы определяем их в строке, используя директиву ng-template
(чтобы мы могли воссоздать работающую демонстрацию), но в идеале они должны быть в отдельных файлах HTML:
<script type="text/ng-template" id="./components/first/first.html"> <h3>{{first.message}}</h3> </script> <script type="text/ng-template" id="./components/second/second.html"> <h3>{{second.message}}</h3> </script>
Как представления очень просты, так и контроллеры:
angular.module('simpleRouterDemo') .controller('FirstController', function(){ console.log('FirstController loaded'); this.message = 'This is the first controller! You are in the first view.'; }) .controller('SecondController', function($routeParams){ console.log('SecondController loaded'); this.message = 'Hey ' + $routeParams.name + ', you are now in the second view!'; });
Поскольку оба этих контроллера созданы для использования с Controller as
синтаксиса, они не принимают $scope
. Служба $routeParams
используется для получения значений параметров, переданных по маршруту.
Теперь нам нужно загрузить этот контроллер для регистрации маршрутов:
<body ng-app=" simpleRouterDemo" ng-controller="routeController as route" > </body>
Наконец, нам нужно связать эти маршруты и загрузить их на страницу. Новый маршрутизатор ng-link
директивы ng-link
и ng-viewport
, которые связывают представления и загружают шаблоны соответственно. Директива ng-viewport
аналогична директиве ng-view
; это заполнитель для части вашего приложения, загружаемой динамически в зависимости от конфигурации маршрута.
В следующем фрагменте показано использование этих директив:
<div class="col-md-3"> <ul class="nav"> <li> <a ng-link="first">First</a> </li> <li> <a ng-link="second({ name:route.name })">Second</a> </li> </ul> </div> <div class="col-md-9"> <ng-viewport></ng-viewport> </div>
Работа с параллельными представлениями
Директива ng-viewport
может использоваться любое количество раз внутри приложения. Следовательно, можно определить несколько параллельных представлений на странице. Окна просмотра должны иметь уникальные идентификаторы, чтобы загружать в них компоненты посредством определения маршрута.
В следующем фрагменте HTML показано, как включить несколько директив области просмотра:
<div class="col-md-3"> <ul class="nav"> <li><a ng-href="#/{{route.name}}">First and Second</a></li> <li><a ng-href="#/swap/{{route.name}}">Second and First</a></li> </ul> </div> <div class="col-md-9"> <div class="col-md-6" ng-viewport="left"></div> <div class="col-md-6" ng-viewport="right"></div> </div>
Представьте, что мы хотим поместить этот код в папку с именем parallel
и шаблоны представления внутри parallel/components
. Это даст нам:
projectRoot/ parallel/ components/ first/ first.html second/ second.html
Поскольку организация кода будет отличаться от соглашения (по умолчанию Angular ищет папку components
в корне проекта), мы должны указать маршрутизатору искать представления в новой папке. Следующий блок конфигурации делает это:
angular.module('parallelRouterDemo', ['ngNewRouter']) .config(['$componentLoaderProvider', function($componentLoaderProvider){ $componentLoaderProvider.setTemplateMapping(function (name) { return 'parallel/components/' + name + '/' + name + '.html'; }); }])
Маршруты для этой страницы должны загружать два компонента и отображать их в разных видовых экранах. Объект конфигурации использует уникальные идентификаторы видовых экранов, чтобы указать, где и где будет отображаться шаблон представления.
$router.config([ { path: '/:name', component: { left: 'first', right: 'second' } }, { path: '/swap/:name', component: { left: 'second', right: 'first' } }, { path: '/', redirectTo: '/there' } ])
Управление жизненным циклом компонентов
Новая система маршрутизации позволяет нам перехватывать жизненный цикл компонентов и управлять навигацией . Перехват может быть применен путем добавления одной из следующих функций к экземпляру контроллера. Эти функции могут возвращать либо логические значения, либо обещания. Логическое значение true
или разрешенное обещание будет проходить через событие жизненного цикла, а логическое значение false
или отклоненное обещание отменят дальнейшую операцию.
-
canReactivate
: указывает, можно ли повторно активировать представление. Его можно использовать для сохранения состояния представления и оптимизации времени загрузки представления при последующих запросах. -
canActivate
:canActivate
перед активацией компонента. Активирует компонент, когда возвращается разрешенное обещание илиtrue
и отменяет в противном случае. -
canDeactivate
: запускается перед деактивацией компонента. Выгружает компонент, когда возвращается разрешенное обещание илиtrue
и отменяет в противном случае.
Использование методов жизненного цикла нового маршрутизатора делает жизнь проще, чем это было в Angular 1.x, так как нам не нужно обрабатывать агрегированные события в $scope
для обнаружения события жизненного цикла.
Давайте рассмотрим, как canActivate
метод canActivate
на примере. Этот метод может использоваться, чтобы проверить, может ли пользователь получить доступ к представлению перед его загрузкой.
this.canActivate = function(){ var hasAccess = userAccessInfo.hasAccessToSecondComponent; if(!hasAccess){ $window.alert('You don\'t have access to this view. Redirecting to previous view ...'); } return hasAccess; };
То же самое относится и к методу canDeactivate
. Это может быть использовано для ограничения перехода пользователя со страницы с несохраненными изменениями.
this.canDeactivate = function () { if (this.sampleText) { var alertResult = $window.confirm('You have unsaved changes. Do you want to leave the page?'); return alertResult; } return true; };
Примечание. Вы заметите, что в некоторых случаях предупреждение и диалог подтверждения запускаются дважды. Кажется, это ошибка в роутере, и я открыл проблему на GitHub .
Вывод
Новый маршрутизатор Angular обладает многообещающим набором функций, которые делают его идеальным выбором практически для любого сложного сценария. Хотя работа на маршрутизаторе все еще продолжается, стоит поэкспериментировать с ним сегодня, поскольку, помимо всех приятных функций, которые он предоставляет, новый маршрутизатор обещает упростить переход на Angular 2.