Advertisement
  1. Code
  2. Ionic

Начинаем работать с Ionic: Навигация

Scroll to top
Read Time: 15 min

Russian (Pусский) translation by Ilya Nikov (you can also view the original English article)

Final product imageFinal product imageFinal product image
What You'll Be Creating

Мы уже довольно много всего рассмотрели в этой серии статей, включая навигацию. Тем не менее, Ionic предоставляет несколько компонентов, которые предоставляют дополнительные функции для создания более функциональной навигации. В этом уроке мы добавили в приложение компоненты бокового меню и вкладки, а также рассмотрим некоторые дополнительные услуги, чтобы сделать навигацию нашего приложения более удобной.

Файлы проекта

Файлы этой статьи доступны на GitHub. Общая идея приложения заключается в том, что оно показывает некоторую информацию о местных объектах. В этом уроке мы добавляем возможность показывать библиотеки, музеи, парки и больницы. В настоящее время оно показывает только местоположения в Чикаго, что мы исправим в следующем уроке.

Вы можете загрузить завершенный проект для этого учебника с GitHub. Если вы клонируете проект, вы также можете начать писать код с помощью Git выполнив git checkout -b start. Финальный пример также доступен для предварительного просмотра.

Обратите внимание, что я удалил решение из отображения места, которое у нас было в третьей части этой серии. Я не хочу подробно освещать это, но контроллер теперь загружает данные и упрощает навигацию.

1. Добавление бокового меню

Одним из наиболее распространенных навигационных шаблонов в мобильных приложениях является боковое меню. Это ящик, который выдвигается сбоку и выдает навигационные ссылки и, возможно, другое содержимое, такое как текущий статус входа. По дизайну они за пределами экрана и открываются какой-то кнопкой, часто это значок гамбургера, хотя люди не согласны с использованием этой иконки.

Ionic Side MenuIonic Side MenuIonic Side Menu

Боковые меню часто можно открыть, проскользнув сбоку, чтобы открыть его или перевести назад, чтобы закрыть его. Это может быть удобно, но иногда может мешать другим жестам, и вы должны следить за конфликтующими поведением. Вы должны подумать о лучшем использовании прокрутки с учетом всего видения и опыта вашего приложения, и если есть какая-то проблема, вы можете отключить его.

Ionic предоставляет пару компонентов, которые делают тривиальным создание бокового меню. Вы можете создать до двух боковых меню, один справа и один слева. Боковое меню состоит из нескольких компонентов: ionSideMenus, ionSideMenu и ionSideMenuContent.

Чтобы увидеть это в действии, давайте обновим www/index.html и настроим боковое меню. Вы замените существующий контент кодом ниже, который добавит компоненты бокового меню вокруг нашего существующего кода.

1
<body ng-app="App">
2
  <ion-side-menus>
3
    <ion-side-menu side="left">
4
      <ion-header-bar>
5
        <h1 class="title">Civinfo</h1>
6
      </ion-header-bar>
7
      <ion-content>
8
        <ion-list>
9
          <ion-item ui-sref="places" menu-close>Places</ion-item>
10
          <ion-item ui-sref="settings.preferences" menu-close>Settings</ion-item>
11
        </ion-list>
12
      </ion-content>
13
    </ion-side-menu>
14
    <ion-side-menu-content drag-content="false">
15
      <ion-nav-bar class="bar-balanced">
16
        <ion-nav-buttons side="left">
17
          <button menu-toggle="left" class="button button-icon icon ion-navicon"></button>
18
        </ion-nav-buttons>
19
        <ion-nav-back-button class="button-clear">
20
          <i class="ion-arrow-left-c"></i> Back
21
        </ion-nav-back-button>
22
      </ion-nav-bar>
23
      <ion-nav-view></ion-nav-view>
24
    </ion-side-menu-content>
25
  </ion-side-menus>
26
</body>

Чтобы включить боковое меню, мы начинаем с упаковки содержимого приложения в ionSideMenus. Это позволяет Ionic координировать боковое меню и области содержимого. Затем у нас есть ionSideMenu с атрибутом side="left", чтобы указать, на какой стороне он расположен.

В боковом меню мы можем поместить любой контент, который пожелаем. В этом случае, и, возможно, это наиболее распространенный сценарий, контент представляет собой компонент ionHeaderBar и компонент ionList для отображения названия приложения и списка ссылок соответственно. Мы еще не определили настройку, так что ссылка на данный момент не сработает. Также обратите внимание, что компоненты ionItem имеют атрибут menu-close. Это автоматически закрывает боковое меню, когда пользователь нажимает на ссылку, в противном случае она остается открытой.

Компонент ionSideMenuContent используется для содержания основной области содержимого. Эта область содержимого занимает весь экран, но этот компонент просто помогает компоненту бокового меню правильно отображаться. Мы также использовали атрибут drag-content = "false", чтобы отключить жесты перетаскивания, поскольку они будут препятствовать прокручиванию списка и вкладкам.

Мы также добавили новую кнопку в навигационную панель, используя ionNavButtons. Это значок бокового меню, который отображается в верхнем правом углу в виде трех уложенных линий. Эта кнопка имеет атрибут menu-toggle = "left", который запускает меню левой стороны для переключения при выборе.

Теперь, когда наше боковое меню на месте, давайте приступим к настройке следующего основного навигационного компонента, добавив вкладки для представления настроек.

2. Вкладки с индивидуальной навигационной историей

Вкладки - еще один общий шаблон навигации для навигации по приложению. Вкладки легко понять, потому что мы видим их во многих типах интерфейсов, а не только для мобильных приложений.

Вкладки могут быть устаревшими или неактивными. Вкладка, которая отображает контент, который не сохраняет память о каких-либо изменениях, является вкладкой без состояния, в то время как вкладка, которая поддерживает состояние, основанное на взаимодействии с пользователем, является работоспособной (например, сохраняя результат поиска). Мы рассмотрим, как создавать вкладки с сохранением состояния с помощью Ionic, поскольку они более сложны и мощнее.

Ionic TabsIonic TabsIonic Tabs

Настроить вкладки довольно легко с компонентами ionTabs и ionTab. Подобно боковым меню, вы помещаете столько вкладок внутри, сколько хотите. Нет жесткого предела, но я считаю, что пять - это здоровый максимум. На небольших устройствах слишком много значков затрудняет выбор вкладки.

Мы собираемся создать вкладки, создав пару новых файлов. Сначала давайте настроим шаблон, создав новый файл по адресу www/views/settings/settings.html. Добавьте следующий код в новый файл.

1
<ion-tabs class="tabs-icon-top tabs-stable">
2
3
  <ion-tab title="Preferences" icon-on="ion-ios-gear" icon-off="ion-ios-gear-outline" ui-sref="settings.preferences">
4
    <ion-nav-view name="preferences"></ion-nav-view>
5
  </ion-tab>
6
7
  <ion-tab title="About" icon-on="ion-ios-information" icon-off="ion-ios-information-outline" ui-sref="settings.about">
8
    <ion-nav-view name="about"></ion-nav-view>
9
  </ion-tab>
10
11
</ion-tabs>

Компонент ionTabs используется для обертывания внутренних компонентов ionTab. Существует несколько классов, которые могут определять, как появляются вкладки, например, добавлять вкладки вверху или внизу, используя значки с титрами или без них и многое другое. Здесь мы решили использовать вкладки, у которых есть заголовок с пиктограммой вверху со стабильной настройкой цвета.

Компонент ionTab имеет ряд атрибутов, которые могут использоваться для определения его поведения. Он поддерживает множество функций, таких как показ небольшого значка уведомления, привязка вкладок к состояниям, поведение значков и многое другое. Для наших вкладок каждый имеет title, класс значка, когда активна вкладка (icon-on) или неактивна (icon-off), и ссылки на состояние с использованием ui-sref.

Внутри каждой вкладки находится другой ionNavView. Это может показаться неуместным, поскольку у нас уже есть ionNavView, созданный в index.html. То, что мы делаем, объявляет дополнительные местоположения, которые можно отобразить для состояния, которые в свою очередь можно рассматривать как дочерние.

Каждая вкладка может иметь свою собственную историю навигации, потому что каждый ionNavView не зависит от других. Каждая вкладка также имеет уникальное имя, которое пригодится, поэтому мы можем определить определенные состояния, которые появятся в названии ionNavView.

Возможно, вы заметили, что на этой странице нет элемента ionView, и это важно отметить при использовании вкладок с состоянием. Это не нужно, если вы используете ionTabs таким образом, только если вы используете вкладки без состояния, версию компонента CSS, вам это понадобится.

Теперь нам нужно настроить некоторые дополнительные состояния, чтобы сделать пример работоспособным. Создайте еще один файл в www/views/settings/settings.js и добавьте к нему следующий код.

1
angular.module('App')
2
.config(function($stateProvider, $urlRouterProvider) {
3
  $stateProvider.state('settings', {
4
    url: '/settings',
5
    abstract: true,
6
    templateUrl: 'views/settings/settings.html'
7
  })
8
  .state('settings.about', {
9
    url: '/about',
10
    views: {
11
      about: {
12
        templateUrl: 'views/settings/tab.about.html'
13
      }
14
    }
15
  })
16
  .state('settings.license', {
17
    url: '/license',
18
    views: {
19
      about: {
20
        templateUrl: 'views/settings/tab.license.html'
21
      }
22
    }
23
  })
24
  .state('settings.preferences', {
25
    url: '/preferences',
26
    views: {
27
      preferences: {
28
        controller: 'PreferencesController',
29
        controllerAs: 'vm',
30
        templateUrl: 'views/settings/tab.preferences.html'
31
      }
32
    }
33
  });
34
35
  $urlRouterProvider.when('/settings', '/settings/preferences');
36
})
37
.controller('PreferencesController', function(Types) {
38
  var vm = this;
39
40
  vm.types = Types;
41
});

Вы можете видеть, что мы создаем несколько новых состояний, но они отличаются от других состояний, которые мы определили до сих пор. Первое состояние представляет собой абстрактное состояние, которое по существу является состоянием, которое не может быть непосредственно загружено самостоятельно. Это имеет смысл для нас с интерфейсом табуляции, поскольку состояние settings загружает шаблон компонентов вкладки, но пользователи никогда не находятся только в компоненте вкладки. Они всегда просматривают активную вкладку, содержащую другое состояние. Таким образом, использование абстрактных дает нам возможность правильно их подключать.

Остальные три состояния определяются как settings.[name]. Это позволяет нам определить отношения между родителями и дочерними элементами между этими состояниями, которые в основном отражают отношения между родителями и дочерними компонентами ionTabs и ionTab. В этих состояниях используется свойство view, которое представляет собой объект со свойством, названным по представлению, которое он использует.

Имя, которое вы указываете в своем шаблоне с помощью ionNavView, должно совпадать с именем свойства. Значением этого свойства является то же определение состояния, без url, который был объявлен обычным способом. url также следует за отношениями родитель-ребенок, объединяя их. Таким образом, все эти дочерние состояния отображаются как /settings/preferences.

Вам нужно добавить settings.js в index.html с помощью другого тега script. Как только вы это сделаете, вы увидите некоторые ошибки, потому что мы ссылаемся на несколько файлов, которые мы еще не создали. Давайте закончим наши шаблоны вкладок.

1
<script src="views/settings/settings.js"></script>

Нам нужно создать три. Первые два являются статическим контентом, поэтому я не буду подробно на них останавливаться. Создайте файл на www/views/settings/tab.about.html и добавьте к нему следующий контент.

1
<ion-view view-title="About" hide-back-button="true">
2
  <ion-content>
3
    <div class="list">
4
      <a href="https://github.com/gnomeontherun/civinfo-part-3" target="_blank" class="item">
5
        <h2>Project on GitHub</h2>
6
        <p>Click to view project</p>
7
      </a>
8
      <div class="item" ui-sref="settings.license">
9
        <h2>License</h2>
10
        <p>See full license</p>
11
      </div>
12
    </div>
13
  </ion-content>
14
</ion-view>

Он содержит шаблон, который отображает некоторую информацию. Он связан с проектом GitHub и лицензией. Вот как это выглядит.

About TabAbout TabAbout Tab

Создайте еще один файл в www/views/settings/tab.license.html и добавьте к нему следующий контент.

1
<ion-view view-title="License">
2
  <ion-content>
3
    <div class="card">
4
      <div class="item item-divider">
5
        The MIT License (MIT)
6
      </div>
7
      <div class="item item-text-wrap">
8
        <p>Copyright (c) 2016 Jeremy Wilken</p>
9
        <p>Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:</p>
10
        <p>The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.</p>
11
        <p>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</p>
12
      </div>
13
    </div>
14
  </ion-content>
15
</ion-view>

Он содержит содержимое лицензии (MIT) для этого кода. Существует простая карточка для содержимого контента. Вот как это выглядит.

License StateLicense StateLicense State

Окончательный шаблон содержит некоторые элементы формы. Я рассмотрю это немного подробнее. Создайте новый файл www/views/settings/tab.preferences.html и добавьте к нему следующий контент.

1
<ion-view view-title="Preferences" hide-back-button="true">
2
  <ion-content>
3
    <ul class="list">
4
      <li class="item item-divider">
5
        Types of Locations
6
      </li>
7
      <li class="item item-toggle" ng-repeat="type in vm.types">
8
        {{type.type}}
9
        <label class="toggle">
10
          <input type="checkbox" ng-model="type.enabled">
11
          <div class="track">
12
            <div class="handle"></div>
13
          </div>
14
        </label>
15
      </li>
16
    </ul>
17
  </ion-content>
18
</ion-view>

Это представление содержит список переключателей, которые показывают четыре типа мест, которые приложение может отображать, музей, парк, библиотеку и больницу. Каждый из этих элементов списка позволяет вам включать или отключать тип места из списка. Кнопка переключения является компонентом CSS. Нам просто нужно использовать флажок с этой специфической разметкой и структурой классов CSS, чтобы они отображались как мобильные переключающие кнопки.

Preference Toggle ButtonsPreference Toggle ButtonsPreference Toggle Buttons

В этом представлении есть контроллер, объявленный в settings.js, но он использует службу Types, которую мы еще не создали. Мы исправим это, добавив новый сервис в www/js/app.js.

1
.factory('Types', function() {
2
  return [
3
    {type: 'Park', enabled: true},
4
    {type: 'Hospital', enabled: true},
5
    {type: 'Library', enabled: true},
6
    {type: 'Museum', enabled: true}
7
  ];
8
})

Эта служба содержит массив типов мест. Он имеет свойство для имени каждого типа места и флаг является ли он включенным или отключен. Мы используем свойство enabled в кнопке переключения ngModel для отслеживания состояния, если этот тип должен отображаться.

На этом этапе вы можете открыть боковое меню и перейти к ссылке настроек. Вы можете видеть две вкладки, настройки и другое. На вкладке «Настройки» вы можете включать или отключать типы мест.

Если вы перейдете на вкладку about, вы можете выбрать лицензию, чтобы увидеть, как она перемещается по другому маршруту на вкладке. Если вы переключитесь между настройками и вкладкой с лицензией после просмотра лицензии, вы увидите, что на вкладке запоминается, что вы были в состоянии лицензии даже после того, как вы ушли, демонстрируя состояние этих вкладок с точки зрения состояния.

Последним этапом этого урока является обновление представления мест для использования службы Types для загрузки только нужных типов мест и использования службы истории для обработки, когда нужно перезагрузить или использовать кеш.

3. Кэширование и использование службы истории

По умолчанию Ionic кэширует последние 10 просмотров и сохраняет их в памяти. Во многих приложениях может быть даже не так много состояний, что означает, что все ваше приложение может оставаться в памяти. Это полезно, потому что это означает, что Ionic не нужно повторно отображать представление перед навигацией, что ускоряет работу приложения.

Это может вызвать некоторые поведенческие проблемы, поскольку вы можете подумать, что ваши состояния всегда перезагружают и повторно инициализируют контроллер в любое время, когда к нему обращаются. Поскольку кэшируется только 10 просмотров, если у вас 20 просмотров, в кеше будут только 10 последних. Это означает, что вы не можете гарантировать, что представление находится в кеше или нет. Поэтому вы должны избегать выполнения логики установки в своих контроллерах за пределами хуков жизненного цикла. Вы также можете настроить стратегии кэширования с помощью $ionicConfigProvider.

Иногда вам нужно посмотреть навигационную историю пользователя, чтобы определить, что делать. Например, в этом приложении мы хотим сохранить список мест, кешированных, если пользователь нажимает на место и затем возвращается обратно в список. Если мы автоматически обновляем список при каждом посещении, пользователи могут потерять свое место в списке после прокрутки и просмотра места.

С другой стороны, если пользователь переходит на страницу настроек, а затем обратно в список мест, мы хотим обновить список, поскольку они могут изменить типы мест, которые они хотят отобразить.

Мы собираемся использовать комбинацию событий жизненного цикла, которые мы рассмотрели ранее, с помощью службы $ionicHistory, чтобы добавить некоторую логику, которая поможет определить, когда состояние мест должно перезагрузить список. Мы также хотим использовать службу Types, чтобы помочь нам загружать только те типы мест, которые пользователь хочет увидеть.

Откройте www/views/places/places.js и обновите его, чтобы он соответствовал следующему коду. Нам нужно изменить способ загрузки данных и использовать службу $ionicHistory для проверки истории, чтобы определить, когда нужно перезагружать.

1
angular.module('App')
2
.config(function($stateProvider) {
3
  $stateProvider.state('places', {
4
    url: '/places',
5
    controller: 'PlacesController as vm',
6
    templateUrl: 'views/places/places.html'
7
  });
8
})
9
.controller('PlacesController', function($http, $scope, $ionicLoading, $ionicHistory, Geolocation, Types) {
10
  var vm = this;
11
  var base = 'https://civinfo-apis.herokuapp.com/civic/places?location=' + Geolocation.geometry.location.lat + ',' + Geolocation.geometry.location.lng;
12
  var token = '';
13
  vm.canLoad = true;
14
  vm.places = [];
15
16
  vm.load = function load() {
17
    $ionicLoading.show();
18
    var url = base;
19
    var query = [];
20
    angular.forEach(Types, function(type) {
21
      if (type.enabled === true) {
22
        query.push(type.type.toLowerCase());
23
      }
24
    });
25
    url += '&query=' + query.join('|');
26
27
    if (token) {
28
      url += '&token=' + token;
29
    }
30
31
    $http.get(url).then(function handleResponse(response) {
32
      vm.places = vm.places.concat(response.data.results);
33
      token = response.data.next_page_token;
34
35
      if (!response.data.next_page_token) {
36
        vm.canLoad = false;
37
      }
38
      $scope.$broadcast('scroll.infiniteScrollComplete');
39
      $ionicLoading.hide();
40
    });
41
  };
42
43
  $scope.$on('$ionicView.beforeEnter', function() {
44
    var previous = $ionicHistory.forwardView();
45
    if (!previous || previous.stateName != 'place') {
46
      token = '';
47
      vm.canLoad = false;
48
      vm.places = [];
49
      vm.load();
50
    }
51
  });
52
});

Во-первых, мы изменили способ создания URL-адреса для нашего API для перехода к загрузке запрошенных типов. Если вы сравните это с предыдущей версией, то заметите что в используется angular.forEach, чтобы перебрать каждый тип и добавить его в URL.

Мы также изменили способ работы системы $ionicLoading. Вместо запуска сразу, когда контроллер запускается изначально, мы запускаем его в любое время, когда вызывается метод vm.load(). Это важно, потому что контроллер будет кэшироваться и не будет перезагружать данные по умолчанию.

Самым большим изменением является обработчик события $ionicView.beforeEnter. Это событие срабатывает до того, как представление вот-вот станет следующим активным видом и позволит нам сделать некоторые настройки. Мы используем $ionicHistory.forwardView() Чтобы получить информацию о последнем просмотре, в котором находился пользователь.

Если это первая загрузка, тогда он будет пустым, иначе он вернет некоторые данные о последнем состоянии. Затем мы проверяем, было ли предыдущее состояние состоянием места, и если да, то мы используем список результатов кэширования. Кроме того, поскольку мы имеем менее 10 состояний, мы знаем, что состояние всегда будет храниться в памяти.

В противном случае он сбросит кешированные значения и инициирует новую загрузку данных. Это означает, что когда я вернусь к просмотру мест после перехода к настройкам, он перезагрузит данные. В зависимости от дизайна вашего приложения вы, вероятно, захотите разработать различные условные правила для обработки кэширования и перезагрузки.

Служба истории предоставляет дополнительную информацию, такую как весь стек истории, возможность изменения истории, сведения о текущем состоянии и многое другое. Вы можете использовать эту службу для точной настройки поведения во время навигации в приложении.

Мы собираемся сделать две другие небольшие настройки в нашем шаблоне мест. Откройте www/views/places/places.html и измените заголовок на Локальные Места.

1
<ion-view view-title="Local Places" hide-back-button="true">

Затем обновите бесконечный элемент прокрутки с помощью еще одного атрибута, immediate-check, чтобы предотвратить бесконечный компонент прокрутки от загрузки данных одновременно с начальной загрузкой. Это существенно предотвращает дублирование запросов на получение большего количества данных.

1
<ion-infinite-scroll on-infinite="vm.load()" ng-if="vm.canLoad" immediate-check="false"></ion-infinite-scroll>

На данный момент мы создали довольно солидное приложение, имеющее довольно приятный набор функций. Мы завершим эту серию одниой последней учебной статьей о Cordova  и интеграции с некоторыми функциями устройства, такими как доступ к данным GPS.

Заключение

Навигация с помощью Ionic всегда начинается с объявления некоторых состояний. Как мы видели в этом руководстве, предоставление этой навигации может быть сделано несколькими способами. Это то, что мы рассмотрели в этом уроке:

  • Компоненты бокового меню упрощают создание одного или двух боковых меню, которые могут быть активированы по требованию или при свайпе.
  • Вкладки могут быть с состоянием и без. В закладках с состоянием могут быть отдельные виды с отдельными навигационными историями.
  • Вкладки имеют множество опций настройки для отображения значков и текста.
  • Кнопка переключения - это компонент CSS, который работает как флажок, но предназначен для мобильных устройств.
  • Вы можете использовать службу $ionicHistory, чтобы узнать больше о навигационной истории приложения для максимальной настройки его поведения.
Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.