简介:在本节课中,我们将会通过一个虚构的旅游景点来构建一款功能完善的应用。本应用的核心特性是管理用户的应用内导航。本节课的主要目的,是展现构建一个完整的应用的过程。

  无论是什么移动应用,最重要的功能之一都是让用户可以在应用内导航,首先需要设置应用导航必要的基础部分,然后继续使用Ionic用户界面组件来构建新界面,所有组件协同工作,让应用可以显示天气信息,游客的预订信息以及附近的景点信息,应用还会使用一个简单的幻灯片来做新手指引,这个在许多应用中我们都有见到。

  下面我们先来展示一下基本的 应用流程图:

  

  下面进入开发环节:

  4.1 配置项目

  我们先来配置项目,基础环境,我在第二节课的时候已经讲过了,现在先来看看版本:

  

  我们先来创建一个空的项目,执行命令:

  

  安装完成后进入文件夹:

  

  启动服务:

  

  我们会看到这样一个页面:

  

  4.2 配置应用导航

  开始构建应用之前,我们来看下用户可以访问的视图都有哪些~

  

  以上图片展示的只是概览,我们需要构建每个视图,下面我们先来配置应用导航,然后再给每个视图添加内容。Ionic支持第三方路由框架ui-router,它是导航的中央大脑,Ionic就是在ui-router基础上开发的,所以不需要关心底层细节,除非要开发自定义的导航功能。

  现在,我们来理解一下导航和路由的概念。导航是指用户在应用内部移动的动作,路由是指应用内部的过程,用于控制用户导航时具体的行为。换句话来解释,就是导航时用户的行为,路由是应用响应用户输入的逻辑。

  我们的第一个任务是向应用的index.html文件添加一个Ionic导航组件,然后再声明一个起始视图。

  ionNavView和ionNavBar是Ionic的基础组件,用于导航。ionNavView就像一个占位符,用于把不同的视图内容载入应用,ionNavBar是标题栏,在用户跳转新视图时自动更新。这两个组件是一同工作的,不过如果你不想要顶部的导航栏,也可以单独使用ionNavView。ionNavBar在应用的顶部,我们可以在这里放置当前视图的标题,也可以放置按钮,如果返回按钮,在我们的应用中,会使用ionNavBackButton组件来实现一种返回方式。

  打开index.html文件,加入导航组件:

<!-- 把angular应用插入页面 -->
<body ng-app="starter">
<!-- 声明ionNavBar,并且添加bar-positive类 -->
<ion-nav-bar class="bar-positive">
<!-- 声明ionNavBackButton,如果有上一级视图就会出现 -->
<ion-nav-back-button class="button-clear">
<!-- 用返回箭头图标来当做返回按钮 -->
<i class="icon ion-ios-arrow-left"></i> Back
</ion-nav-back-button>
<!-- 声明ionNavView,这里会显示每个视图的内容 -->
</ion-nav-bar>
<ion-nav-view></ion-nav-view>
</body>

  我们现在运行代码,会发现页面上什么功能都没有,这是因为我们还没有声明任何视图。然后我们需要来声明应用的一些列状态,状态是ui-router的一个概念,状态对应应用当前需要显示的视图,其中会包括视图对应的URL、视图控制器的名字和视图对应的模板。

  下面我们来声明第一个状态-->home状态,打开www/js/app.js文件:

angular.module('starter', ['ionic'])
//添加新的config方法并注入$stateProvider
.config(function($stateProvider,$urlRouterProvider){
//声明第一个状态对应的是主视图
$stateProvider.state('home', {
//给状态设置一个URL,可以用在锚点连接上
url:'/home',
//视图激活时,让这个状态从指定的URL加载模板
templateUrl:'view/home/home.html'
});
//声明降级URL,如果应用找不到请求的状态会跳转到这里
$urlRouterProvider.otherwise('/home');
})
.run(function($ionicPlatform) {...

  上面的代码中,我们使用$stateProvider服务来声明状态,用$urlRouterProvider在请求无效的时候指定跳转地址。otherwise()方法非常重要,因为它会在应用无法找到目标路由时发挥作用,比如王章的404错误。如果用户试图请求一个不存在的状态,otherwise()方法会显示主视图,所以最好保证有otherwise()方法。

  我们在上面声明了一个模板,但是没有创建对应的文件,下面我们就来创建主视图文件。创建新文件www/views/home/home.html文件并写入如下代码:

<ion-view view-title="旅游啦" hide-back-button="true">
</ion-view>

  现在运行代码,我们可以看到这样的页面:

  

  注意hide-back-button属性,它控制视图是否可见返回按钮。

  4.3 构建主视图

  现在项目只有一个带标题的空视图,我们需要给视图添加内容。这时,我们需要用到ionContent来创建内容容器,这是一个通用的内容封装器,它有很多特性:根据设备调整内容区域的尺寸、和头部底部协同合作、管理滚动。最常用的就是管理滚动。下面我们打开home.html文件,加入如下内容:

<ion-view view-title="旅游啦" hide-back-button="true">
<!-- 显示主视图的内容 -->
<ion-content>
</ion-content>
</ion-view>

  现在有了内容容器了,我们需要向应用中添加一个基础的列表组件,还是打开home.html文件,加入如下内容:

<ion-view view-title="旅游啦" hide-back-button="true">
<!-- 显示主视图的内容 -->
<ion-content>
<!-- 给容器元素添加list类,从而指定它为列表容器 -->
<div class="list">
<!-- 给元素添加item类,从而创建一个列表元素,这里它会链接到其他视图 -->
<a href="#/reservation" class="item">预订信息</a>
<a href="#/weather" class="item">本地天气</a>
<a href="#/restaurants" class="item">附近餐馆</a>
</div>
</ion-content>
</ion-view>

  现在页面效果如下图所示:

  

  现在,我们来给主视图的列表项添加图标,ionic自带许多的图标,它们被称为ionicons。

  打开home.html文件,我们来给主视图添加图标:

<ion-view view-title="旅游啦" hide-back-button="true">
<!-- 显示主视图的内容 -->
<ion-content>
<!-- 给容器元素添加list类,从而指定它为列表容器 -->
<div class="list">
<!-- 给元素添加item类,从而创建一个列表元素,这里它会链接到其他视图,添加item-icon-left类来获取我们想要的效果 -->
<a href="#/reservation" class="item item-icon-left">
<!-- 给i元素添加图标类,就可以展示图标了 -->
<i class="icon ion-document-text"></i> 预订信息
</a>
<a href="#/weather" class="item item-icon-left">
<i class="icon ion-ios-partlysunny"></i> 本地天气
</a>
<a href="#/places" class="item item-icon-left">
<i class="icon ion-fork"></i> 附近景点
</a>
</div>
</ion-content>
</ion-view>

  主视图效果如下图所示:

  

  4.4 使用控制器和模型来开发预订视图

  对于预订视图,由于我们需要加载用户的数据并显示出来,所以可以使用控制器来管理数据。下面我们先来创建一个新的控制器,创建文件www/views/reservation/reservation.js:

//引入angular模块
angular.module('starter')
//声明控制器的名字和函数,接受一个元素到列表并注入$scope中
.controller('ReservationController', function($scope){
//把模型对象赋值给$scope
$scope.reservation = {
//设定停留日期
checkin:new Date(),
checkout:new Date(Date.now()+1000*60*60*24*7),
//设置预订需要的其他静态值
room:208,
rate:121,
wifi:'208wifi',
wificode:'888888'
};
});

  下面我们来构建一下预订视图,创建www/views/reservation/reservation.html文件:

<ion-view view-title="预订信息">
<ion-content>
<!-- 使用列表组件包裹列表 -->
<div class="list">
<div class="item item-icon-left">
<!-- 设置图标,把数据绑定到模板中 -->
<i class="icon ion-key"></i>
房间号:{{reservation.room}}
</div>
<div class="item item-icon-left">
<i class="icon ion-calendar"></i>
入住时间:{{reservation.checkin | date:'mediumDate'}}
</div>
<div class="item item-icon-left">
<i class="icon ion-calendar"></i>
离开时间:{{reservation.checkout | date:'mediumDate'}}
</div>
<div class="item item-icon-left">
<i class="icon ion-wifi"></i>
WIFI:{{reservation.wifi}}
</div>
<div class="item item-icon-left">
<i class="icon ion-wifi"></i>
WIFICode:{{reservation.wificode}}
</div>
<div class="item item-icon-left">
<i class="icon ion-pricetag"></i>
单价:{{reservation.rate | currency}}/晚
</div>
<div class="item item-icon-left">
<i class="icon ion-pricetags"></i>
总价:{{reservation.rate * 7 | currency}}
</div>
</div>
</ion-content>
</ion-view>

  现在,我们有了预订视图以及将数据显示出来,剩下的只需要把这个视图添加到应用的状态管理器当中,打开app.js文件,接着主视图添加以下代码:

            //声明预览视图的状态
.state('reservation', {
//使用/reservation URL来标识这个状态
url: '/reservation',
//声明这个视图用到的控制器的名称
controller: 'ReservationController',
//声明要加载的视图文件
templateUrl: 'views/reservation/reservation.html'
});

  然后,把reservation.js文件引入到index.html文件的</head>标签前面。此时,我们的预订视图已经完成了,预览效果如下图:

  

  4.5 把数据加载到天气视图中

  现在我们来完成天气视图,天气视图的功能是从外部服务器载入天气数据,所以我们先来配置控制器来加载外部天气数据。我们需要使用到Angular提供的$http服务来从一个URL加载数据,把$http服务注入到控制器,访问一个URL,然后处理HTTP请求的成功或者失败的情况。首先我们先来创建一个新的控制器文件www/views/weather/weather.js:

//引用angular模块
angular.module('starter')
//声明控制器并注入$scope和$http服务
.controller('WeatherController', function($scope, $http){
//声明包含所有可能风向的数组
var directions = ['北','东北','东','东南','南','西南','西','西北'];
//发起HTTP请求来从给定的URL加载数据
$http.get('http://ionic-in-action-api.herokuapp.com/weather')
//处理响应成功的情况,并获取返回天气对象
.success(function(weather){
//把天气数据赋值给$scope.weather模型
$scope.weather=weather;
})
//在这里做错误处理
.error(function(err){});
//用来把角度值转换成风向的方法
$scope.getDirection = function(degree){
if(degree > 338){
degree = 360 - degree;
}
var index = Math.floor((degree+22)/45);
return directions[index];
};
});

  然后我们来给添加视图添加一个模板,创建文件www/views/weather/weather.html:

<ion-view view-title="天气情况">
<!-- 用容器包裹内容 -->
<ion-content>
<div class="list">
<!-- 添加列表元素,绑定天气对象的数据 -->
<div class="item">天气:{{weather.weather[0].main}}</div>
<div class="item">温度:{{weather.main.temp}}&deg;</div>
<div class="item">空气湿度:{{weather.main.humidity}}%</div>
<div class="item">最高气温:{{weather.main.temp_max}}&deg;</div>
<div class="item">最低气温:{{weather.main.temp_min}}&deg;</div>
<div class="item">
<!-- 风向元素有两个绑定数据,第二个会调用作用域中的第一个方法 -->
风向:{{weather.wind.speed}}mph,{{getDirection(weather.wind.deg)}}
</div>
</div>
</ion-content>
</ion-view>

  然后我们来声明天气视图状态,打开app.js文件,在预订视图下面添加如下代码:

            //声明天气视图状态
.state('weather',{
//给声明的状态添加URL、控制器和模板值
url:'/weather',
controller:'WeatherController',
templateUrl:'views/weather/weather.html'
});

  现在我们运行一下吧,预览结果如下图:

  

  此时当我们执行应用的时候,会发现视图会有一个加载过程,页面数据会出现一个短暂的空白时期,知道数据加载完毕,这样的用户体验并不理想,所以我们需要加载一个载入指示器。但是在展示加载动画的时候用户无法操作应用,所以要考虑清楚是否要这样做。现在我们先来实现一下如何加载动画。

  加载组件有两个方法:show()和hide()。我们需要在HTTP请求执行时显示加载指示器,在响应返回后隐藏它。下面我们打开weather.js文件,做如下修改:

//引用angular模块
angular.module('starter')
//声明控制器并注入$scope和$http服务,把$ionicLoading服务注入控制器中
.controller('WeatherController', function($scope, $http, $ionicLoading){
//声明包含所有可能风向的数组
var directions = ['北','东北','东','东南','南','西南','西','西北'];
//在HTTP请求开始之前显示加载组件
$ionicLoading.show();
//发起HTTP请求来从给定的URL加载数据
$http.get('http://ionic-in-action-api.herokuapp.com/weather')
//处理响应成功的情况,并获取返回天气对象
.success(function(weather){
//把天气数据赋值给$scope.weather模型
$scope.weather=weather;
//如果响应成功,隐藏加载组件
$ionicLoading.hide();
})
//在这里做错误处理
.error(function(err){
//如果出错,使用加载器来显示错误信息并在三秒后自动关闭
$ionicLoading.show({
template:'无法加载天气,请稍候再试',
duration:3000
});
});
//用来把角度值转换成风向的方法
$scope.getDirection = function(degree){
if(degree > 338){
degree = 360 - degree;
}
var index = Math.floor((degree+22)/45);
return directions[index];
};
});

  4.6 在餐馆视图中使用卡片和无限滚动

  下面我们进入景点视图的制作。我们需要显示一些本地的餐馆共游客参考,要实现这个功能,需要从外部加载餐馆数据,并使用卡片组件来展示每个餐馆的名字和图片,同时使用无限滚动这样用户滚动到列表底部时会加载更多的信息。

  首先我们先来构建餐馆视图的模板文件,新建文件www/views/restaurants/restaurants.html:

<ion-view view-title="附近餐馆">
<ion-content>
<!-- 创建卡片列表,使用ngRepeat遍历餐馆 -->
<div class="list card" ng-repeat="restaurant in restaurants">
<div class="item">
<h2>{{restaurant.name}}</h2>
<p>{{restaurant.address}},{{restaurant.city}}</p>
</div>
<div class="item item-image">
<img ng-src="{{restaurant.image_url}}" />
</div>
</div>
<!-- 无限滚动元素会在快要滑到底部的时候调用getRestaurants(),除非已经没有新数据了 -->
<ion-infinite-scroll on-infinite="getRestaurants()" ng-if="total > page" immediate-check="false">
</ion-infinite-scroll>
</ion-content>
</ion-view>

  然后我们需要给餐馆视图添加一个控制器。这个控制器需要加载餐馆数据并在新数据加载完毕时通知唔想滚动组件隐藏。创建文件www/views/restaurant/restaurant.js:

angular.module('starter')
//创建控制器并注入服务
.controller('RestaurantsController', function($scope, $http) {
//创建一些视图的作用域变量
$scope.page = 0;
$scope.total = 1;
$scope.restaurants = [];
//定义加载餐馆的方法
$scope.getRestaurants = function() {
//递增页数并发起HTTP请求
$scope.page++;
$http.get('http://ionic-in-action-api.herokuapp.com/restaurants?page=' + $scope.page).success(function(response) {
//获取餐馆列表并把它们添加到ngRepeat操作的餐馆数组中
angular.forEach(response.restaurants, function(restaurant) {
$scope.restaurants.push(restaurant);
});
//基于API的值更新总页数
$scope.total = response.totalPages;
//广播事件,告诉无限滚动组件已经加载完成了
$scope.$broadcast('scroll.infiniteScrollComplete');
//处理错误,广播事件并打印出错误报告
}).error(function(err) {
$scope.$broadcast('scroll.infiniteScrollComplete');
console.log(err);
});
};
//载入页面的时候从API加载第一页餐馆数据
$scope.getRestaurants();
});

  最后,在app.js文件中把视图添加到状态中:

            .state('restaurants', {
url:'/restaurants',
controller:'RestaurantsController',
templateUrl:'views/restaurants/restaurants.html'
});

  我们来预览一下效果:

  

  4.7 使用幻灯片组件来实现应用介绍

  我们希望用户在第一次使用我们的旅游应用时能看到一个快速入门的幻灯片介绍。$ionSlideBoxDelegate服务可以用来在程序中控制幻灯片。大多数情况下,我们只需要一些HTML标签就可以展示幻灯片组件。新建文件www/views/tour/tour.html:

<ion-view view-title="旅游啦" id="tour-view">
<ion-nav-buttons side="right">
<a class="button button-clear" href="#/home" nav-clear>goin</a>
</ion-nav-buttons> <ion-slide-box show-pager="true">
<ion-slide>
<span class="icon icon-slide ion-document-text"></span>
<h3>查看预订信息</h3>
</ion-slide>
<ion-slide>
<span class="icon icon-slide ion-fork"></span>
<h3>查看附近餐馆</h3>
</ion-slide>
<ion-slide>
<span class="icon icon-slide ion-ios-sunny"></span>
<h3>查看本地天气</h3>
</ion-slide>
</ion-slide-box>
</ion-view>

  然后我们稍微修改一下幻灯片视图的样式,新建文件www/views/tour/tour.css:

#tour-view .slider{
height: 100%;
}
#tour-view .slider-slide{
padding-top:100px;
text-align: center;
}
#tour-view .icon-slide{
font-size: 20em;
display: inline-block;
}

  把tour.css引入index.html文件中:

<link href="views/tour/tour.css" rel="stylesheet">

  最后我们来把幻灯片视图添加到应用的状态中去,打开app.js文件,添加新状态:

            .state('tour',{
url:'/tour',
templateUrl:'views/tour/tour.html'
});

  现在预览一下我们的应用吧~

  

4 Ionic导航和核心组件--旅游应用的更多相关文章

  1. ionic导航之后返回功能的说明

    当我导航view之后,再使用$location.path("/path/origin")方法重新定位到初始页面,在深入进入其他的view之后使用这个方法就遇到了问题. 假设这个设置 ...

  2. Google Map和桌面组件 Android开发教程

    本文节选于机械工业出版社推出的<Android应用开发揭秘>一 书,作者为杨丰盛.本书内容全面,详细讲解了Android框架.Android组件.用户界面开发.游戏开发.数据存储.多媒体开 ...

  3. 从NMEA0183到GNSS定位数据获取(一)原理篇

    作者:良知犹存 转载授权以及围观:欢迎添加微信公众号:Conscience_Remains 总述 GPS我们都知道,一种用来全球定位的系统,后来俄罗斯推出了格洛纳斯定位系统,中国推出了北斗定位,欧盟有 ...

  4. ionic之AngularJS扩展 移动开发(视图导航一)

    目录: 内联模板 : script 路由机制 : 状态机 导航视图 : ion-nav-view 模板视图 : ion-view 导航栏 : ion-nav-bar 回退按钮 : ion-nav-ba ...

  5. ionic 进入多级目录以后隐藏底部导航栏(tabs)(完美解决方案)

    公司开始使用ionic开发项目,在此记录下把遇到的问题,网上有大牛已经把解决方法整出来了,不过记录在自己这里方便查阅. 这篇记录在有tabs的项目里,进入子层级时,底部导航还一直存在,本人是要让他只在 ...

  6. Ionic Js十二:导航ion-nav-view

     ion-nav-view 当用户在你的app中浏览时,ionic能够检测到浏览历史.通过检测浏览历史,实现向左或向右滑动时可以正确转换视图. 采用AngularUI路由器模块等应用程序接口可以分为 ...

  7. 在第一段ionic示例的基础上增加底部导航

    demo2.html <!DOCTYPE html> <html ng-app="app"> <head> <meta charset=& ...

  8. ionic 进入二级目录以后隐藏底部导航栏(tabs)

    1.在标签ion-tabs中添加:ng-class=”{‘tabs-item-hide’: $root.hideTabs}”,源码如下: <ion-tabs class="tabs-i ...

  9. 关注Ionic底部导航按钮tabs在android情况下浮在上面的处理

    Ionic是一款流行的移动端开发框架,但是刚入门的同学会发现,Ionic在IOS和android的底部tabs显示不一样.在安卓情况下底部tabs会浮上去. 如下图展示:  网上也有很多此类的解决方案 ...

随机推荐

  1. bzoj4951 [Wf2017]Money for Nothing

    题目描述 题解: 答案显然是$max((q-p)*(e-d))$ 依然先贪心. 对于工厂,我们倾向于$pi<pj,di<dj$的; 对于买家,我们倾向于$qi>qj,ei>ej ...

  2. CSRF verification failed. Request aborted. 表单提交方法为POST时的报错

    本人所用Django版本为1.11,在设置请求方法为POST时,遇到标题中的错误,尝试了多种方法,最终通过下面的操作来修复: 在template文件中添加图中红框部分 接着,导入csrf_exempt ...

  3. static静态方法的优缺点

    static可以修饰成员变量,成员方法,代码块,类特点: static修饰的方法和变量,为类所属方法和变量,不会在对象销毁时销毁,所以生命周期较长.被static修饰的内容会随着类的加载而加载,优先于 ...

  4. Vue编写轮播组件引入better-scroll插件无法正常循环轮播

    临近过年还是发个博客表示一下自己的存在感,这段时间公司突然说想搞小程序,想到这无比巨大的坑就只能掩面而泣,于是乎这段时间在学习小程序开发.关于标题所说的是有老铁问的,我也跟着网上的代码码了一遍然后发现 ...

  5. hadoop格式化出错,提示IO异常

    配置好hadoop之后,在进行格式化的时候出现异常,原因是由于在core-site.xml 配置文件中写的路径格式不对. 不需要加 file:/ 或者 file:// 直接写绝对路径就行. <c ...

  6. 潜伏者(codevs 1171)

    题目描述 Description [问题描述]R 国和S 国正陷入战火之中,双方都互派间谍,潜入对方内部,伺机行动.历尽艰险后,潜伏于 S 国的R 国间谍小C 终于摸清了S 国军用密码的编码规则:1. ...

  7. 2016 Multi-University Training Contest 3 solutions BY 绍兴一中

    1001 Sqrt Bo 由于有\(5\)次的这个限制,所以尝试寻找分界点. 很容易发现是\(2^{32}\),所以我们先比较输入的数字是否比这个大,然后再暴力开根. 复杂度是\(O(\log\log ...

  8. [NOIP1998] 提高组 洛谷P1011 车站

    题目描述 火车从始发站(称为第1站)开出,在始发站上车的人数为a,然后到达第2站,在第2站有人上.下车,但上.下车的人数相同,因此在第2站开出时(即在到达第3站之前)车上的人数保持为a人.从第3站起( ...

  9. virtualBox下Centos系统扩展磁盘空间

    (1)查看空间容量: 打开windows命令终端.然后打开virtualbox安装目录,找到VBoxManage.exe,拖动到终端里面.输入命令:list hdds,回车. 我安装的位置是 : C: ...

  10. 线程&线程池

    线程 进程和线程: 进程只是用来把资源集中到一起(进程只是一个资源单位,或者说资源集合),而线程才是cpu上的执行单位. 注意:两个都是过程 线程一个特点: 一个进程中,多个线程共享资源 线程和进程区 ...