Роутинг в Angular.js

Illustration of a person sitting on the ground, leaning against a wall, engaged in reading a book. The figure has sketched lines and is wearing casual clothes with orange sneakers. Illustration of a person sitting on the ground, leaning against a wall, engaged in reading a book. The figure has sketched lines and is wearing casual clothes with orange sneakers.

Мы подходим к концу нашего руководства по Angular.js. Осталось всего две статьи: эта и ещё одна. В этой мы рассмотрим Angular.js UI Router – библиотеку для организации навигации между различными частями нашего приложения. Заодно мы перепишем наш код так, чтобы он использовал шаблоны, вместо хранения всего html в одном файле.

Шаблоны в нашем приложении хранятся в папке src/app/app_part/. Под app_part я имею ввиду отдельную часть приложения. Сейчасу нас только main, но в будущем могут быть так же подпапки settings, categories и что угодно ещё. Перенесём всю центральную часть, содержащую форму для добавления транзакций и таблицу с транзакциями в src/app/main/transactions.html и заменим её на:

<div class="col-xs-6" ui-view></div>

Так же необходимо убрать ui-view из контейнера уровнем выше. Таким образом в src/index.html внутри тега body помимо тегов script у нас остаётся следующая вёрстка:

<div class="container">
  <div class="col-xs-2" ng-controller="NavigationCtrl as navigation_ctrl">
    <h2>Деньги</h2>
    <h3 class="money-ok">
      {{navigation_ctrl.transactions_store.sum()}}
    </h3>
    <ul class="nav nav-pills nav-stacked">
      <li>
        <a>
          <i class="glyphicon glyphicon-th-list"></i>
          Транзакции <span class="badge">{{navigation_ctrl.transctions_store.transactions.length}}</span>
        </a>
      </li>
      <li>
        <a>
          <i class="glyphicon glyphicon-tasks"></i> Настройки
        </a>
      </li>
    </ul>
  </div>
  <div class="col-xs-6" ui-view></div>
</div>

Теперь у нас ничего не работает, потому что мы не указали UI Router’у какой шаблон выводить в ui-view по корневой ссылке. Именно этим данная библиотека и занимается: вставляет в ui-view шаблон, соответствующий настройкам приложения.

Итак, обновим src/app/index.js:

'use strict';
angular.module('ngmkdev', ['restangular', 'ui.router'])
  .config(function ($stateProvider, $urlRouterProvider) {
    $stateProvider
    .state('transactions', {
      url: "/",
      templateUrl: "app/main/transactions.html"
    });
    $urlRouterProvider.otherwise('/');
  });

Мы используем функцию config() для конфигурации различных провайдеров (один из видов сервисов) до того как они начнут использоваться в других частях приложения.

Здесь через цепочку вызовов функции state() на сервисе $stateProvider мы указываем какой шаблон выводить внутри ui-view для каждой страницы. Последняя строчка, $urlRouterProvider.otherwise('/'); говорит Angular’у на какой адрес перенаправлять пользователя если запрошенный маршрут не существует. Наше приложение теперь снова должно работать как и прежде.

Немного аналогий для знакомых с Ruby on Rails: ui-view можно сравнить с yield в рельсовых шаблонах, а цепочку вызовов state() – с файлом routes.rb.

Попробуем добавить настоящую навигацию: чтобы ссылка Транзакции вела на таблицу с транзакциями, а Настройки на будущую страницу настроек пользователя.

Обновим наш список маршрутов:

'use strict';
angular.module('ngmkdev', ['restangular', 'ui.router'])
  .config(function ($stateProvider, $urlRouterProvider) {

    $stateProvider
    .state('transactions', {
      url: "/transactions",
      templateUrl: "app/main/transactions.html"
    })
    .state('settings', {
      url: "/settings",
      templateUrl: "app/settings/settings.html"
    })

    $urlRouterProvider.otherwise('/transactions');
  });

И добавим файл app/settings/settings.html с таким содержанием:

<h2>Настройки</h2>

Теперь на любой несуществующий путь будет происходить редирект на страницу с транзакциями. Попробуйте открыть следующие пути: http://localhost:3000/, http://localhost:3000/#/transactions, http://localhost:3000/#/settings, http://localhost:3000/#/crap.

Толку от ручного вбивания адресов немного, поэтому оживим ссылки в боковом меню. Для этого в UI Router существует директива ui-sref, в которой указывается название стейта (первый аргумент функции state()), который должен открыться по клику на ссылке:

<li>
  <a ui-sref="transactions">
    <i class="glyphicon glyphicon-th-list"></i>
    Транзакции <span class="badge">{{navigation_ctrl.transctions_store.transactions.length}}</span>
  </a>
</li>
<li>
  <a ui-sref="settings">
    <i class="glyphicon glyphicon-tasks"></i> Настройки
  </a>
</li>

Этого достаточно чтобы наше меню заработало – по клику на ссылки центральная часть приложения меняется. Коммит с изменениями, проделанными в статье: 448daee.

Задание на дом:
  • Найдите в документации UI Router способ подсвечивать текущий пункт меню