Статьи

Начало работы с Ionic: навигация

Конечный продукт
Что вы будете создавать

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

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

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

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

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

Ионное боковое меню

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

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

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<body ng-app=»App»>
  <ion-side-menus>
    <ion-side-menu side=»left»>
      <ion-header-bar>
        <h1 class=»title»>Civinfo</h1>
      </ion-header-bar>
      <ion-content>
        <ion-list>
          <ion-item ui-sref=»places» menu-close>Places</ion-item>
          <ion-item ui-sref=»settings.preferences» menu-close>Settings</ion-item>
        </ion-list>
      </ion-content>
    </ion-side-menu>
    <ion-side-menu-content drag-content=»false»>
      <ion-nav-bar class=»bar-balanced»>
        <ion-nav-buttons side=»left»>
          <button menu-toggle=»left» class=»button button-icon icon ion-navicon»></button>
        </ion-nav-buttons>
        <ion-nav-back-button class=»button-clear»>
          <i class=»ion-arrow-left-c»></i> Back
        </ion-nav-back-button>
      </ion-nav-bar>
      <ion-nav-view></ion-nav-view>
    </ion-side-menu-content>
  </ion-side-menus>
</body>

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

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

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

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

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

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

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

Ионные вкладки

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

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

01
02
03
04
05
06
07
08
09
10
11
<ion-tabs class=»tabs-icon-top tabs-stable»>
 
  <ion-tab title=»Preferences» icon-on=»ion-ios-gear» icon-off=»ion-ios-gear-outline» ui-sref=»settings.preferences»>
    <ion-nav-view name=»preferences»></ion-nav-view>
  </ion-tab>
 
  <ion-tab title=»About» icon-on=»ion-ios-information» icon-off=»ion-ios-information-outline» ui-sref=»settings.about»>
    <ion-nav-view name=»about»></ion-nav-view>
  </ion-tab>
 
</ion-tabs>

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

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

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

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

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

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

01
02
03
04
05
06
07
08
09
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
35
36
37
38
39
40
41
angular.module(‘App’)
.config(function($stateProvider, $urlRouterProvider) {
  $stateProvider.state(‘settings’, {
    url: ‘/settings’,
    abstract: true,
    templateUrl: ‘views/settings/settings.html’
  })
  .state(‘settings.about’, {
    url: ‘/about’,
    views: {
      about: {
        templateUrl: ‘views/settings/tab.about.html’
      }
    }
  })
  .state(‘settings.license’, {
    url: ‘/license’,
    views: {
      about: {
        templateUrl: ‘views/settings/tab.license.html’
      }
    }
  })
  .state(‘settings.preferences’, {
    url: ‘/preferences’,
    views: {
      preferences: {
        controller: ‘PreferencesController’,
        controllerAs: ‘vm’,
        templateUrl: ‘views/settings/tab.preferences.html’
      }
    }
  });
 
  $urlRouterProvider.when(‘/settings’, ‘/settings/preferences’);
})
.controller(‘PreferencesController’, function(Types) {
  var vm = this;
 
  vm.types = Types;
});

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

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

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

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

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

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
<ion-view view-title=»About» hide-back-button=»true»>
  <ion-content>
    <div class=»list»>
      <a href=»https://github.com/gnomeontherun/civinfo-part-3″ target=»_blank» class=»item»>
        <h2>Project on GitHub</h2>
        <p>Click to view project</p>
      </a>
      <div class=»item» ui-sref=»settings.license»>
        <h2>License</h2>
        <p>See full license</p>
      </div>
    </div>
  </ion-content>
</ion-view>

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

О вкладке

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
<ion-view view-title=»License»>
  <ion-content>
    <div class=»card»>
      <div class=»item item-divider»>
        The MIT License (MIT)
      </div>
      <div class=»item item-text-wrap»>
        <p>Copyright (c) 2016 Jeremy Wilken</p>
        <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>
        <p>The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.</p>
        <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.
      </div>
    </div>
  </ion-content>
</ion-view>

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

Состояние лицензии

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
<ion-view view-title=»Preferences» hide-back-button=»true»>
  <ion-content>
    <ul class=»list»>
      <li class=»item item-divider»>
        Types of Locations
      </li>
      <li class=»item item-toggle» ng-repeat=»type in vm.types»>
        {{type.type}}
        <label class=»toggle»>
          <input type=»checkbox» ng-model=»type.enabled»>
          <div class=»track»>
            <div class=»handle»></div>
          </div>
        </label>
      </li>
    </ul>
  </ion-content>
</ion-view>

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

Кнопки переключения предпочтений

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

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

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

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

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

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

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

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

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

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

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

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

01
02
03
04
05
06
07
08
09
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
angular.module(‘App’)
.config(function($stateProvider) {
  $stateProvider.state(‘places’, {
    url: ‘/places’,
    controller: ‘PlacesController as vm’,
    templateUrl: ‘views/places/places.html’
  });
})
.controller(‘PlacesController’, function($http, $scope, $ionicLoading, $ionicHistory, Geolocation, Types) {
  var vm = this;
  var base = ‘https://civinfo-apis.herokuapp.com/civic/places?location=’ + Geolocation.geometry.location.lat + ‘,’ + Geolocation.geometry.location.lng;
  var token = »;
  vm.canLoad = true;
  vm.places = [];
 
  vm.load = function load() {
    $ionicLoading.show();
    var url = base;
    var query = [];
    angular.forEach(Types, function(type) {
      if (type.enabled === true) {
        query.push(type.type.toLowerCase());
      }
    });
    url += ‘&query=’ + query.join(‘|’);
 
    if (token) {
      url += ‘&token=’ + token;
    }
 
    $http.get(url).then(function handleResponse(response) {
      vm.places = vm.places.concat(response.data.results);
      token = response.data.next_page_token;
 
      if (!response.data.next_page_token) {
        vm.canLoad = false;
      }
      $scope.$broadcast(‘scroll.infiniteScrollComplete’);
      $ionicLoading.hide();
    });
  };
 
  $scope.$on(‘$ionicView.beforeEnter’, function() {
    var previous = $ionicHistory.forwardView();
    if (!previous || previous.stateName != ‘place’) {
      token = »;
      vm.canLoad = false;
      vm.places = [];
      vm.load();
    }
  });
});

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

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

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

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

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

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

Мы собираемся сделать еще два небольших изменения в нашем шаблоне мест. Откройте www / views / place / place.html и измените заголовок на Local Places .

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 чтобы узнать больше об истории навигации приложения, чтобы настроить $ionicHistory с пользователем.