• 日常搜索
  • 百度一下
  • Google
  • 在线工具
  • 搜转载

Ionic入门:导航

我们已经在本系列中介绍了很多内容,包括导航。但是,ionic 提供了一些组件,这些组件为构建更多功能的导航提供了附加功能。在本教程中,我们将侧边菜单和选项卡组件添加到应用程序中,我们还研究了一些额外的服务,以使我们的应用程序的导航更智能。

教程项目文件

教程项目文件可在GitHub上找到。该应用程序的一般前提是它显示了有关当地设施的一些信息。在本教程中,我们添加了显示图书馆、博物馆、公园和医院的功能。目前,它只显示芝加哥的位置,这是我们在下一个教程中修复的问题。

您可以从GitHub下载本教程的完整项目 。如果您克隆项目,您还可以使用 Git 并运行git checkout –b start. 最后一个示例也可供预览。

请注意,我已从本系列第三部分中的位置视图中删除了解析。我不想深入介绍它,但控制器现在加载数据并使我们的导航更简单。

1.添加侧边菜单

移动应用程序中最常见的导航模式之一是侧边菜单。这是一个从侧面滑出的抽屉,它显示导航链接和可能的其他内容,例如当前登录状态。按照设计,它们在屏幕外并通过某种按钮打开,通常是汉堡图标,即使人们不同意该图标的使用。

Ionic入门:导航  第1张

侧边菜单通常可以通过从侧面滑动将其拉开或向后滑动以将其关闭来打开。这可能很方便,但有时会妨碍其他手势,您应该留意相互冲突的行为。您应该考虑到应用程序的整个愿景和体验的最佳使用方式,如果有问题,您可以禁用它。

Ionic 提供了几个组件,可以轻松设置侧边菜单。您最多可以创建两个侧边菜单,一个在右侧,一个在左侧。侧边菜单包含几个组件,ionSideMenus、ionSideMenu和ionSideMenuContent。

要查看实际情况,让我们更新www/index.html 并设置一个侧边菜单。您将使用下面的代码替换现有内容,这会在我们现有代码周围添加侧菜单组件。

<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 带有side="left"属性来指定它占据哪一边。

在侧边菜单中,我们可以放置我们想要的任何内容。在这种情况下,可能是最常见的场景,内容是一个 ionHeaderBar组件和一个ionList组件,分别用于呈现应用程序标题和链接列表。我们尚未定义设置视图,因此该链接暂时会失败。另请注意,ionItem组件具有menu-close属性。当用户单击链接时,这会自动关闭侧面菜单,否则它会保持打开状态。

该ionSideMenuContent组件用于包含主要内容区域。这个内容区域占据了整个屏幕,但是这个组件只是帮助侧边菜单组件正确渲染。我们还使用该 drag-content="false"属性来禁用拖动手势,因为它们会干扰滚动列表和选项卡。

我们还使用 向导航栏添加了一个新按钮ionNavButtons。这是在右上角显示为三个堆叠行的侧面菜单图标。此按钮具有 menu-toggle="left" 属性,该属性会触发左侧菜单在选择时切换。

现在我们的侧边菜单已经到位,让我们通过为设置视图添加选项卡来设置下一个主要导航组件。

2. 带有单独导航历史的标签

选项卡是导航应用程序的另一种常见导航模式。标签很容易理解,因为我们在很多类型的界面中看到它们,而不仅仅是移动应用程序。

选项卡可以是有状态的或无状态的。显示不保留任何更改记忆的内容的选项卡是无状态的,而基于用户交互维护状态的选项卡是有状态的(例如,保留搜索结果)。我们看看如何使用 Ionic 构建有状态的选项卡,因为它们更复杂、更强大。

Ionic入门:导航  第2张ionTabs使用和ionTab组件设置选项卡相当容易。就像侧面菜单一样,您可以在其中放置任意数量的选项卡组件。没有硬性限制,但我发现五个是健康的最大值。在较小的设备上,太多的图标使选择标签变得困难。

我们将通过创建几个新文件来设置选项卡。首先,让我们通过在www/views/settings/settings.html创建一个新文件来设置模板。将以下代码添加到新文件中。

<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)或非活动状态(icon-off),并使用 链接到状态ui-sref。

每个选项卡中都有另一个ionNavView. 这可能看起来不合适,因为我们已经在index.htmlionNavView中进行了设置。我们正在做的是声明可以呈现状态的其他位置,可以将其视为子视图。

每个选项卡都可以有自己的导航历史,因为每个选项卡都ionNavView独立于其他选项卡。每个选项卡还有一个唯一的名称,它会派上用场,因此我们可以定义某些状态以出现在命名的 ionNavView.

您可能已经注意到此页面上没有ionView元素,并且在使用有状态选项卡时要注意这一点。这种方式使用的时候不需要ionTabs,只有使用无状态标签,css组件版本,才需要。

我们现在需要设置一些额外的状态来使示例正常运行。在www/views/settings/settings.js创建另一个文件,并将以下代码添加到其中。

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 。完成此操作后,您将看到一些错误,因为我们引用了许多尚未创建的文件。让我们完成我们的标签模板。

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

我们需要创建三个。前两个是静态内容,所以我不打算详细介绍它们。在www/views/settings/tab.about.html创建一个文件,并在其中添加以下内容。

<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 项目和许可证。这就是它的样子。

Ionic入门:导航  第3张

在www/views/settings/tab.license.html创建另一个文件,并在其中添加以下内容。

<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. 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>
      </div>
    </div>
  </ion-content>
</ion-view>

这包含此代码的许可内容 (MIT)。有一张简单的卡片来包含内容。这就是它的样子。

Ionic入门:导航  第4张最终模板包含一些表单元素。我将更详细地讨论它。在www/views/settings/tab.preferences.html创建一个新文件,并在其中添加以下内容。

<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 类结构的复选框输入来使它们显示为移动切换按钮。

Ionic入门:导航  第5张此视图在settings.js中声明了一个控制器,但它注入了一个 Types我们尚未创建的服务。我们将通过向www/js/app.js添加新服务来解决这个问题。

.factory('Types', function() {
  return [
    {type: 'Park', enabled: true},
    {type: 'Hospital', enabled: true},
    {type: 'Library', enabled: true},
    {type: 'Museum', enabled: true}
  ];
})

该服务包含一系列地点类型。它具有每个地点类型的名称以及是否启用或禁用的属性。ngModel如果应该显示该类型,我们使用切换按钮中的启用属性 来跟踪状态。

此时,您可以打开侧面菜单并导航到设置链接。您可以看到两个选项卡、首选项和关于。在首选项选项卡中,您可以打开或关闭位置类型。

如果您转到关于选项卡,您可以选择许可证以查看它如何导航到选项卡中的另一条路线。如果您在查看许可证后在首选项和关于选项卡之间切换,您可以看到即使在您离开后,该选项卡也会记住您处于许可证状态,这证明了这些选项卡的状态性质。

本教程的最后一步是更新场所视图,以使用该 Types服务仅加载所需类型的场所,并使用历史服务来处理何时重新加载或使用缓存。

3.缓存和使用历史服务

默认情况下,Ionic 缓存最后 10 个视图并将它们保存在内存中。许多应用程序甚至可能没有那么多状态,这意味着您的整个应用程序可能会保留在内存中。这很有用,因为这意味着 Ionic 在导航之前不必再次渲染视图,从而加快了应用程序的速度。

这可能会导致一些行为问题,因为您可能认为您的状态总是在访问状态时重新加载并重新初始化控制器。由于只有 10 个视图被缓存,如果您有 20 个视图,则只有最后 10 个会在缓存中。这意味着您不能保证视图是否在缓存中。因此,您应该避免在生命周期挂钩之外的控制器中执行设置逻辑。您还可以使用$ionicConfigProvider.

有时您需要查看用户的导航历史以确定要做什么。例如,在这个应用程序中,如果用户点击某个地点然后返回列表,我们希望保持缓存的地点列表。如果我们在每次访问时自动刷新列表,用户可能会在滚动并查看某个地点后失去其在列表中的位置。

另一方面,如果用户导航到设置页面,然后返回地点列表,我们希望刷新列表,因为他们可能已经更改了他们希望显示的地点类型。

我们将使用我们之前查看过的生命周期事件与$ionicHistory服务的组合来添加一些逻辑,这将有助于确定场所状态何时应该重新加载列表。我们还希望使用该Types服务来帮助我们仅加载用户希望查看的地点类型。

打开www/views/places/places.js 并更新它以匹配以下代码。我们需要改变数据的加载方式,并使用$ionicHistory服务来检查历史以确定何时重新加载。

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();
    }
  });
});

首先,我们修改了为 API 构建 URL 的方式,将仅加载公园更改为加载请求的类型。如果将此与以前的版本进行比较,它主要 angular.forEach 用于循环每种类型并将其添加到 URL。

我们还修改了$ionicLoading服务的行为方式。我们不是在控制器最初运行时立即运行,而是在vm.load()调用方法时触发它。这很重要,因为控制器将被缓存并且默认情况下不会重新加载数据。

最大的变化是 $ionicView.beforeEnter生命周期事件处理程序。此事件在视图即将成为下一个活动视图之前触发,并允许我们进行一些设置。我们使用该$ionicHistory.forwardView()方法来获取有关用户上次查看的信息。

如果是第一次加载,那么这将为空,否则返回一些关于最后状态的数据。然后我们检查之前的状态是否是地点状态,如果是,我们使用缓存的结果列表。此外,由于我们的状态少于 10 个,我们知道状态将始终保存在内存中。

否则,它将重置缓存值并触发新的数据加载。这意味着每当我在进入设置后返回地点视图时,它都会重新加载数据。根据您的应用程序设计,您可能希望设计不同的条件规则来处理缓存和重新加载。

历史服务提供更多信息,例如整个历史堆栈、修改历史的能力、有关当前状态的详细信息等等。您可以使用此服务在应用程序中导航时微调体验。

我们将对位置模板进行另外两个小调整。打开www/views/places/places.html并将标题更改为Local Places。

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

接下来,使用一个属性更新无限滚动组件 immediate-check,以防止无限滚动组件在初始加载发生的同时加载数据。这基本上有助于防止重复请求更多数据。

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

在这一点上,我们已经构建了一个非常可靠的应用程序,它具有一组非常好的功能。我们将以最后一个教程结束本系列,该教程着眼于 cordova 并与一些设备功能集成,例如访问 GPS 数据。

结论

使用 Ionic 导航总是从声明一些状态开始。正如我们在本教程中看到的那样,可以通过多种方式公开该导航。这就是我们在本教程中介绍的内容:

  • 侧边菜单组件使创建一个或两个侧边菜单变得容易,这些侧边菜单可以按需或滑动激活。

  • 选项卡可以是无状态的或有状态的。有状态的选项卡可以有单独的视图和单独的导航历史。

  • 选项卡有许多用于图标和文本显示方式的配置选项。

  • 切换按钮是一个类似于复选框的 CSS 组件,但它是为移动设备设计的。

  • 您可以使用该$ionicHistory服务来了解有关应用程序导航历史的更多信息以自定义体验。


文章目录
  • 教程项目文件
  • 1.添加侧边菜单
  • 2. 带有单独导航历史的标签
  • 3.缓存和使用历史服务
  • 结论