ionic+cordova+AngularJS混合app开发笔记

ionic 项目

安装项目依赖

已在环境配置里面设置好

打开命令窗口,在搜索中输入cmd
输入npm install cordova ionic -g

快速创建项目

先找到一个放项目的位置
在目录中按住 shift 键右击,在当前文件夹打开命令窗口
ionic start myApp (默认是tab样式)

项目结构说明

1
2
3
4
5
6
7
8
|-- hooks
|-- platform 手机平台
|-- plugins cordova 插件库
|-- resources 图片资源
|-- scss
|-- www 项目资源
|-- .bowerrc bower 配置文件
...

打包app

先找到项目的根目录
在目录中按住shift键右击,在当前文件夹打开命令窗口
Ionic platform add android
Ionic build android

项目结构搭建

要从新整理结构

方便运维,结构清晰,方便查找
方便团队开发

抽取步骤

控制项目启动的 app.js

控制路由跳转的 route.js

控制全局变量的 global.js

控制不同平台兼容性的 config.js

功能模块划分

Controller:业务逻辑

Factory:数据请求访问,和服务器进行操作。

Html页面:功能界面

Route:子功能路由js,控制我们的页面跳转。

外面四个js实现步骤

创建四个控制全局功能的js文件

把每个js文件变成模块

app:run
config: config
global: constant
route: config

把js文件在html中引入

在app.js 文件中注入我们其他三个模块的东西(加入功能)

app.js

1
2
3
4
5
6
7
8
9
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
// Ionic Starter App
angular.module('starter', ['ionic','config','global','route','ionicLazyLoad','indexdb','commonJs','ngCordova'])
.run(function($ionicPlatform,$location,$ionicHistory,$cordovaToast,$rootScope) {
$ionicPlatform.ready(function() {
if (window.cordova && window.cordova.plugins && window.cordova.plugins.Keyboard) {
cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
cordova.plugins.Keyboard.disableScroll(true);
}
if (window.StatusBar) {
// org.apache.cordova.statusbar required
StatusBar.styleDefault();
}
// 给android的物理返回按钮添加点击事件
// 第一个参数是注册的事件,第二个参数是注册事件的优先级
$ionicPlatform.registerBackButtonAction(function(e){
if($rootScope.backButtonPressedOnceToExit){
ionic.Platform.exitApp();
}
else {
if($location.path()=="/tab/home"||$location.path()=="/tab/category"||$location.path()=="/tab/account"||$location.path()=="/tab/cart"){
$rootScope.backButtonPressedOnceToExit=true;
$cordovaToast.showShortBottom('再点一次退出!');
setTimeout(function(){
$rootScope.backButtonPressedOnceToExit=false;
},2000)
}
else {
$ionicHistory.goBack();
}
}
e.preventDefault();
return false
},110);
});
});

route.js

通过 angular.module.config 方法定义路由

引入 $stateProvider(), $urlRouterProvider

就是根据浏览器url地址后面 # 部分后面你的变化匹配不同的路由,匹配到合适的路由之后,就会把 templateUrl 中写得页面模板渲染到我们页面中的<ion-vav-view>,如果页面中有很多的<ion-nav-view>组件,他就会根据组件中的name属性的值进行选择渲染。Controller属性中的控制器会被自动渲染到模板页面中,变为ng-controller="控制器名称"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
angular.module('route', [
'guidePage.route',
'tab.route',
'home.route',
'category.route',
'goodsList.route',
'details.route',
'cart.route',
'account.route'
])
.config(function($stateProvider, $urlRouterProvider) {
// 第一次登陆
if(localStorage["isFirst"])
{
$urlRouterProvider.otherwise('/tab/home');
}
else {
$urlRouterProvider.otherwise('/guidePage');
}
});

Global.Js

通过 angular.module.constant 定义成模块,不经常改变的东西放在这里面。

如果要使用,在其他模块中注入服务就可以了,比如 GlobalVariable,在其他模块中就可以把里面的属性都点出来。

1
2
3
4
5
6
7
8
// 全局变量模块
angular.module('global', [])
.constant("GlobalVariable",{
'HTTP': 'https',
'SERVER_PATH':'http://192.168.1.1',// 服务器地址
'VERSION':"0.0.1",
'PORT':'8080'
});

Config.js

通过 angular.module.config 定义成模块

注入 $ionicConfigProvider 服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 配置模块,控制不同平台的兼容性
angular.module('config', [])
.config(function($ionicConfigProvider){
$ionicConfigProvider.platform.android.tabs.position("bottom");
$ionicConfigProvider.platform.ios.tabs.position("bottom");
//$ionicConfigProvider.platform.ios.tabs.style('standard');
//$ionicConfigProvider.platform.ios.tabs.position('bottom');
//$ionicConfigProvider.platform.android.tabs.style('standard');
//$ionicConfigProvider.platform.android.tabs.position('bottom');
//$ionicConfigProvider.platform.ios.navBar.alignTitle('center');
//$ionicConfigProvider.platform.android.navBar.alignTitle('center');
//
//$ionicConfigProvider.platform.ios.backButton.previousTitleText('').icon('ion-ios-arrow-thin-left');
//$ionicConfigProvider.platform.android.backButton.previousTitleText('').icon('ion-android-arrow-back');
//
//$ionicConfigProvider.platform.ios.views.transition('ios');
//$ionicConfigProvider.platform.android.views.transition('android');
})

ng-app --(starter)--> app.js --> 引进其它模块

功能模块 js 的整理步骤

1、 创建四个文件

Controller文件:控制业务逻辑的
Route文件:功能模块路由
Service文件:数据请求访问的
Html页面

2、把每个js文件变成模块

Controller: angular.module.controller
route: angular.moduleconfig
service: angular.modulefactory
route: angular.moduleconfig

3、把 js 文件在 html 中引入

4、在模块 js 文件中注入需要的服务和模块

总结:页面启动流程:

命名规范

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
index
<body na-app='starter'> </body>
ionic.bundle.js 文件中有 angular.js
starter 入口模块
'ionic','config','global','route'
'ionicLazyLoad','indexdb','commonJs','ngCordova'
|-- ionic 是 bundle.js 中的内容,是 ionic 框架自定义的
|-- congfig 兼容性配置 .config(function($ionicConfigProvider){})
|-- global constant 的东西执行 .constant("GlobalVariable",{})
|-- route 功能模块的入口文件 .config(function($stateProvider, $urlRouterProvider){})
|-- guidePage.route .config(function($stateProvider, $urlRouterProvider){})
|-- guidePage.controller .controller('GuidePageCtrl',function ($scope, $state){})
|-- guidePage.service .factory('GuidePageFty', function(){})
|-- route 有一个:$urlRouterProvider.otherwise('/guidePage');
<ion-nav-view></ion-nav-view> 模板页面渲染到了它所在的地方

扩展链接

http://ngionic.com/2014/12/ionic-javascript-api-ionicconfigprovider/
http://ngionic.com/2014/12/ionic-javascript-api-ion-view-%E8%A7%86%E5%9B%BE%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F%E5%8F%8A%E4%BA%8B%E4%BB%B6%E9%9B%86%E5%90%88/

ionic css
ionic js: angular 指令
ionic 图标库

路由

Ui-router的介绍

Github地址:https://github.com/angular-ui/ui-router

ui-router 他算是angular中进行路由跳转的一个第三方插件,本质是对 ng-route 进行了一些封装。

通过 npm install angular-ui-router 下载
在 index.html 启动页面中引入 js 文件,引入顺序要注意,因为需要 angular 中的 ng-route 的东西,所以要在引入 angular.js文件之后引入。
需要引入 ui-router 模块
在 html 中的某个标签加上 ui-view 标签,作用就是在页面中挖了一个坑,之后的模板页面就往这里面填。
在 js 文件中编写我们的路由。
根据浏览器地址栏的变化,匹配不同的路由,然后进行模板页面的渲染和控制器的加载。

抽象路由的实现

为了实现在一个页面中可以出现多个坑(模板页面替换位置),才实现了抽象路由的概念

抽象路由的实现

注意点:

抽象路由是不会被单独匹配渲染的,只有配合子路由的实现才能渲染虚拟路由中的模板
子路由中的路由名称中的点是有实际意义的,是为了配合虚拟路由,实现父子路由的层级关系。

ionic中路由实现

在index页面中加入<ion-nav-view></ion-nav-view>标签,在 ionic 中 ui-router 的 ui-view 已经被<ion-nav-view>组件封装了,所以不会出现在页面中。
要写路由模块的配置信息,通过angular.module.config配置路由信息(设置路由名称,url地址,模板页面,controller)
在index.html页面把路由js引进来,在app.js中注入路由模块的功能
把页面的功能包裹在ion-view或者是ion-tab标签中
根据浏览器中url地址的变化,匹配不同的路由
渲染我们的模板页面到相应的<ion-nav-view></ion-nav-view>组件中

tab_router.js

1
2
3
4
5
6
7
8
9
10
11
angular.module('tab.route', ['tab.controller'])
.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('tab', {
url: '/tab',
// 抽象路由
abstract: true,
templateUrl: 'areas/tab/tab.html',
controller: 'TabCtrl'
})
});

home_route.js

1
2
3
4
5
6
7
8
9
10
11
12
13
angular.module('home.route', ['home.controller'])
.config(function ($stateProvider, $urlRouterProvider) {
$stateProvider
.state('tab.home', {
url: '/home',
views: {
'tab-home': {
templateUrl: 'areas/home/home.html',
controller: 'HomeCtrl'
}
}
})
});

. 是有实际意义的:表明是抽象路由的子路由。

此时的路由就是:/tab/home

route.js

1
2
3
4
5
6
7
8
9
10
angular.module('route', [
'guidePage.route',
'tab.route',
'home.route',
'starter.controllers',
'starter.services'
])
.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/tab/home');
});

index.html

1
2
3
4
5
6
7
8
9
10
<!--tab切换-->
<script src="areas/tab/tab_controller.js"></script>
<script src="areas/tab/tab_route.js"></script>
<!--首页-->
<script src="areas/home/home_controller.js"></script>
<script src="areas/home/home_route.js"></script>
<script src="areas/home/home_service.js"></script>
<body ng-app="starter">
<ion-nav-view></ion-nav-view>
</body>

这样现在页面就有四个坑了。

引导页的实现

创建引导页步骤

创建四个功能模块文件
编写模块功能,(这里从之前的代码复制 html 页面和 css 样式)
在 index.html 页面中引入三个文件(controller, route, service)
将功能路由在总路由中注入

Swiper3简介

官网:http://www.swiper.com.cn/

Swier3插件结构:

Swiper

使用swiper步骤

在任意一个位置打开开命令窗口,输入 npm install bower –g
在项目根目录打开命令窗口,输入 bower install swiper –save
在 index.html 页面引入 swiper.min.css,swiper.min.js
把页面中的 html 代码加上对应的 class 样式
在 controller 中初始化 swiper 插件,并设置相应的属性

首页的实现

项目布局改造

创建四个首页面功能文件,修改里面的内容

在 index 页面引入四个文件

在总路由中注入子功能路由,controller, service 也都要在子功能路由中注入

Tab模板改造

对 ion-tabs 里面变为四个 ion-tab s组件
对四个 ion-tab 组件的名称,图标,跳转地址进行了修改,ion-nav-view 组件的 name 属性改掉
把 tabs 的抽象路由给抽取了出来,变为一个功能模块
在 index 页面中引入 tabs 的相关文件
在总路由中注入 tabs.route 模块

动态生成swiper

在controller中模拟请求后台数据,把请求回来的数据放到$scope上
在html页面中通过ng-repeat指令循环生成slider滑动页
初始化swiper对象,注意最好把observeParents,observer这两个属性设置为true.
要注意初始化swiper的时间,最好放在$ionicView.afterEnter中

1
2
3
4
5
6
7
8
9
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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<title></title>
<!--公共css-->
<link href="lib/ionic/css/ionic.css" rel="stylesheet">
<link href="css/common.css" rel="stylesheet">
<!--功能模块css-->
<link href="css/guidePage/guidePage.css" rel="stylesheet">
<link href="css/home/home.css" rel="stylesheet">
<link href="css/category/category.css" rel="stylesheet">
<link href="css/goodsList/goodsList.css" rel="stylesheet">
<link href="css/details/details.css" rel="stylesheet">
<link href="css/cart/cart.css" rel="stylesheet">
<link href="css/account/account.css" rel="stylesheet">
<link href="lib/Swiper/dist/css/swiper.css" rel="stylesheet">
<!-- ionic/angularjs js -->
<script src="lib/ionic/js/ionic.bundle.js"></script>
<!-- cordova script (this will be a 404 during development) -->
<script src="cordova.js"></script>
<!-- 全局js文件 -->
<script src="js/app.js"></script>
<script src="js/route.js"></script>
<script src="js/global.js"></script>
<script src="js/config.js"></script>
<script src="areas/common/indexdb.js"></script>
<script src="areas/common/common.js"></script>
<!--功能模块js-->
<script src="areas/guidePage/guidePage_controller.js"></script>
<script src="areas/guidePage/guidePage_route.js"></script>
<script src="areas/guidePage/guidePage_service.js"></script>
<script src="areas/tab/tab_controller.js"></script>
<script src="areas/tab/tab_route.js"></script>
<script src="areas/home/home_controller.js"></script>
<script src="areas/home/home_route.js"></script>
<script src="areas/home/home_service.js"></script>
<script src="areas/category/category_controller.js"></script>
<script src="areas/category/category_route.js"></script>
<script src="areas/category/category_service.js"></script>
<script src="areas/goodsList/goodsList_controller.js"></script>
<script src="areas/goodsList/goodsList_route.js"></script>
<script src="areas/goodsList/goodsList_service.js"></script>
<script src="areas/details/details_controller.js"></script>
<script src="areas/details/details_service.js"></script>
<script src="areas/details/details_route.js"></script>
<script src="areas/cart/cart_controller.js"></script>
<script src="areas/cart/cart_service.js"></script>
<script src="areas/cart/cart_route.js"></script>
<script src="areas/account/account_controller.js"></script>
<script src="areas/account/account_service.js"></script>
<script src="areas/account/account_route.js"></script>
<!--第三方插件js-->
<script src="lib/Swiper/dist/js/swiper.js"></script>
<script src="lib/jquery/dist/jquery.js"></script>
<script src="lib/ionic-image-lazy-load/ionic-image-lazy-load.js"></script>
<script src="lib/ngCordova/dist/ng-cordova.js"></script>
</head>
<body ng-app="starter">
<!--
The nav bar that will be updated as we navigate between views.
-->
<!--<ion-nav-bar class="bar-stable">-->
<!--<ion-nav-back-button>-->
<!--</ion-nav-back-button>-->
<!--</ion-nav-bar>-->
<!--
The views will be rendered in the <ion-nav-view> directive below
Templates are in the /templates folder (but you could also
have templates inline in this html file if you'd like).
-->
<ion-nav-view></ion-nav-view>
</body>
</html>

ion-view 的生命周期

ion-view的生命周期
ion-view的生命周期

ionic中清理缓存的四种方法

http://ionicframework.com/docs/api/directive/ionNavView/

延迟加载的实现

Imagelazyload:
http://www.cnblogs.com/cloudgamer/archive/2010/03/03/ImagesLazyLoad.html

jquery.lazyload不能在ionic中使用:
http://www.cnblogs.com/yzg1/p/5051554.html

angular中实现延迟加载
http://segmentfault.com/q/1010000002730440?_ea=192608

me-lazyimg
https://github.com/Treri/me-lazyimg/blob/master/me-lazyimg.js

angular-imglazyload
https://www.npmjs.com/package/angular-imglazyload

ionic-image-lazy-load(专门的 ionic 图片延迟加载插件)
https://github.com/paveisistemas/ionic-image-lazy-load

延迟加载插件使用步骤

用 bower 进行下载

1
$ bower install ion-image-lazy-load --save

在 index 页面中引入 ion-image-lazy-load.js 文件

在 app.js 中注入 ionicLazyLoad 服务

延迟加载插件

在 ion-content 组件上加上 lazy-scroll 指令,注意 lazy-scroll 指令只能作用于 Ionic-content 组件上。

延迟加载插件

把滚动容器中所有 img 标签的 src 属性替换为 image-lazy-src

ionic中的页面跳转和参数传递

ionic中的路由跳转方式

通过代码的方式进行跳转

(1)在 controller 里面注入 $state 服务

ionic中的路由跳转方式

(2)在页面中给单击按钮增加单击事件
(3)在事件方法里面调用 $state.go(“路由名称”)

ionic中的路由跳转方式

通过 href 属性进行跳转

注意:写我们跳转的锚记,url地址进行跳转

通过href 属性进行跳转

通过ui-sref属性进行跳转

通过ui-sref属性进行跳转

第一种方式在 js 代码里,比如单击事件中,第二种得拼接 #,第三种直接定义路由名称就可以了。

ionic中的参数传递

先修改路由,在路由中加上参数

ionic中的参数传递

将参数进行传递

(1)ui-sref:ui-sref=”goodsList({typeNumber:1})”
(2)http://my.oschina.net/u/1416844/blog/470741

1
<a href="#/goodsList/34">跳转到商品详细页面</a>

将参数进行传递

(3)代码跳转

1
$state.go('goodsList', {typeNumber:666});

在 controller 里面注入 $stateParams 服务,是一个参数对象

参数传递

参数传递

其他:用 LoaclStorage 传递参数。

数据请求

Angular中的service(父级概念)

包含Service,Factory,Provider三个子级概念,都是返回service(父级概念)对象

三种概念定义模块的使用场景

Factory:返回一个匿名对象,匿名对象中是方法的集合
Service:在一个模块中返回多个服务,适合用service创建模块
Provider:是service的底层实现,angular本身的东西,提供的服务

$http服务使用

$http使用简介
http://www.2cto.com/kf/201506/405137.html

在Factory中注入$http服务

请求使用

$http服务的跨域请求访问

$q服务

$q服务的使用:
http://blog.csdn.net/renfufei/article/details/19174015

$q 服务使用步骤:

首先要子啊factoruy中注入$q服务

在Factory中使用

在Factory中使用

在controller中使用

上一个操作的输入时下一个操作的输入

Promise/A+规范

Promise/A+规范简介

http://www.jb51.net/article/50725.htm

Es6中的promise规范
http://web.jobbole.com/85297/

Promise的实现

因为他只是一个规范,所以在不同的框架或者平台下有不同的实现

Angular:$q服务
Node:q模块,co,then
Es6: Promise, yield
Es7:async await

规范出现的原因

我们不知道异步请求什么时候返回数据,所以我们就需要些回调函数。但是在某些情况下我们需要知道数据是在什么时候返回的,然后进行一些处理。

当我们在异步回调里面处理的操作还是异步操作的时候,这样就形成了异步回调的嵌套

正是为了杜绝以上两种情况的出现,社区出现了 Promise/a+ 规范

总结

Promise/a+出现就是为了解决异步深层嵌套
Promise/a+本质就是改变了一种书写格式

规范的内容是什么(重要)

不管进行什么操作都返回一个 promise 对象,这个对象里面会有一些属性和方法(这个效果类似于 jquery 中的链式编程,返回自己本身)

这个promise有三种状态

Unfulfilled(未完成,初始状态)
Fulfilled(已完成)
Failed(失败、拒绝)

3、这个promise对象的使用时通过then方法进行的调用

原理图

原理图

怎么对promise对象的状态进行改变那

通过 $q 服务的 deffer 方法定义出来一个延迟对象。

deffer 方法定义一个延迟对象

这个延迟对象都有三个方法

resolved方法:当数据请求成功的时候调用,参数是成功返回的数据对象,把 promise 对象 Unfulfilled 状态改为 fulfilled 状态。
rejected方法:当数据请求失败的时候调用,参数是失败的原因,把 promise 对象 Unfulfilled 状态改为 failed 状态。
怎么知道在 promise 中调用哪个回调函数,是通过 promise 对象的状态进行判断的。

Promise/A+规范表现形式

1、可以通过链式编程的方式对异步操作进行同级处理
2、上一个操作的输出值是下一个操作的输入值

完整使用说明

$http 配合 $q 的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
angular.module("goodsList.service", [])
.factory("GoodsListFty", function ($http, $q) {
return {
refreshData: function (typeNumber) {
// 1.创建一个延迟对象
var deferred = $q.defer();
$http.get('data.json').success(function (data, header, config, status) {
// 2. 当请求成功的时候用 resolve 方法请求数据
deferred.resolve(data);
console.log(deferred);
}).error(function (reason, header, config, status) {
// 3. 当请求失败的时候用 reject 方法处理失败原因
deferred.reject(reason);
});
// 4. 返回 promise 对象
return deferred.promise;
}
};
});

在controller中使用

1
2
3
4
5
6
7
8
9
10
11
// 商品列表控制器
angular.module("goodsList.controller", ["goodsList.service"])
.controller("GoodsListCtrl", function ($scope, $stateParams, GoodsListFty) {
var promise = GoodsListFty.refreshData($stateParams.typeNumber);
// 用 then 方法调用数据
promise.then(function (data) {
console.log(data);
}).catch(function (reason) {
console.log(reason);
});
});

下拉刷新

中文文档:http://www.ionic.wang/js_doc-index-id-25.html

ion-refresher 介绍

ion-refresher: 必须在 ion-content 标签里面使用,而且必须紧挨着 ion-content 标签。

使用介绍

添加ion-refersher标签,然后配置里面的属性
实现获取数据触发的方法
在获取数据完毕后停止广播

使用介绍
使用介绍

上拉加载更多

http://www.ionic.wang/js_doc-index-id-29.html

ion-infinite-scroll 介绍

必须在 ion-content 标签里面使用,而且必须紧挨着 ion-content 标签。在底部。

使用介绍

添加ion-infinite-scroll标签,然后配置里面的属性
实现获取数据触发的方法
在获取数据完毕后停止广播

$emit.$on

http://www.tuicool.com/articles/qIBNve

生命周期的问题

如果在 enter 时间中执行刷新方法,会报错,数据还没有出来,可是页面已经渲染,加载更多的距离大于 1% 所以会一直执行

1
2
3
$scope.$on('$ionicView.enter', function (e) {
$scope.func_refreshGoodsList();
});

所以我们要放在beforeEnter事件中执行

1
2
3
$scope.$on('$ionicView.beforeEnter', function (e) {
$scope.func_refreshGoodsList();
});

没有数据提示问题

要添加没有数据的提示,在刷新和加载的方法里面要对返回来的 result 数据进行非空判断,然后修改 pms_isMoreItemsAvailable 的值

1
2
3
<div ng-hide="pms_isMoreItemsAvailable" style="text-align: center">
<span>我去,地主家也没有余粮啊!</span>
</div>

循环加载的问题

通过 ng-if 方法来控制加载更多的无限循环,默认 pms_isMoreItemsAvailable=true

1
2
<ion-infinite-scroll ng-if="pms_isMoreItemsAvailable" on-infinite="func_loadMoreGoodsList()" distance="1%">
</ion-infinite-scroll>

遮罩层

1
2
3
$ionicLoading.show({
template: "正在载入数据,请稍后..."
});

在广播完毕之后执行取消遮罩层

1
2
3
setTimeout(function(){
$ionicLoading.hide();
},1000);

IndexedDb

简介

indexedDB 是 HTML5-WebStorage 的重要一环,是一种轻量级 NOSQL 数据库。相比 web sql(sqlite) 更加高效,包括索引、事务处理和健壮的查询功能。

和 webSQL 的比较

允许快速索引和搜索的对象,所以在 HTML5 的 web 应用程序中,可以有效管理你的数据和高效率的读/写操作。
W3C主推的离线数据库类型,逐渐替代 Web SQL 类型数据库,更新效率高并不断完善。
工作在异步模式下执行每步操作。使用高效率的的 JavaScript 事件驱动模块

操作 indexedDB

原理代码:

1
2
3
4
5
6
7
8
9
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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>indexedDB 使用</title>
</head>
<body>
<script>
// 获取indexdb对象,为了兼容性的写法
// 1、获取对象
window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction;
window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
window.IDBCursor = window.IDBCursor || window.webkitIDBCursor || window.msIDBCursor;
// 2、定义数据库的基本信息
var dbInfo = {
dbName: 'aptdb',
dbVersion: 2021, //用小数会四舍五入,版本号只能越来越大
dbInstance: {}
};
// 3、创建数据库
var dbContent = window.indexedDB.open(dbInfo.dbName, dbInfo.dbVersion);
// 判断数据库版本号是否更新,更加 dbversion 和 dbname 去判断,如果没有发送变化,就不走这个回调方法(创建和修改表结构在这里操作)
// 如果数据库名称和版本号相同,那么该方法只执行一次
dbContent.onupgradeneeded = function (e) {
console.log(e);
// 4、创建数据库 store(表)
var _db = e.target.result;
// 保存表名称的一个数组
var storeNames = _db.objectStoreNames;
if (!storeNames.contains("cart")) {
// 创建一个表结构,第一个参数是表名称
_db.createObjectStore("cart", {
// 必须有个 keyPath 属性,这个属性的值必须是能唯一标识这条数据的值的字段
keyPath: "goodsId", //相当于关系型数据库中的主键
autoIncrement: true
});
}
};
// 打开数据库成功的回调函数
dbContent.onsuccess = function (e) {
// 5、增删改查操作,开启事物,每次只能做一件事情
var _db = e.target.result;
// 创建事物
var trans = _db.transaction(["cart"], "readwrite");
// 用事物获取表
var store = trans.objectStore("cart");
// 虽然可以在一次成功打开请求的操作中同时操作多次增删改查命令,但是不推荐,以为这样前面的操作就不能被监听到了
// 增加数据
var req = store.add({
goodsId: 'df6',
prise: 12.3,
name: "衣服",
size: "M",
age: 99
})
// 修改数据
// var req = store.put({
// goodsId: 'df6',
// prise: 12.79,
// name: "衣服",
// size: "L",
// age: 1000
// }
// 删除数据
// var req = store.delete("df6");
// 查询数据
// var req = store.get("df1");
// 删除所有数据
// var req = store.clear();
// 数据操作成功回调函数
// req.onsuccess = function(e){
// console.log('数据操作成功了');
// }
// 数据添加失败回调函数
// req.onerror = function(e){
// console.log('数据操作失败了');
// }
// 查询所有数据(用了游标)
var cursor = store.openCursor();
var data = [];
cursor.onsuccess = function(e){
var result = e.target.result;
if (result && result !== null) {
data.push(result.value);
// 重新执行 onsuccess 句柄,必须得写这句
result.continue();
}
console.log(data);
};
cursor.onerror = function(){
}
};
// 打开数据库请求失败的回调函数
dbContent.onerror = function (e) {
console.log(3);
}
</script>
</body>
</html>

封装代码:

1
2
3
4
5
6
7
8
9
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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
angular.module('indexeddb', [])
.factory('IndexeddbJs', ['$ionicPopup',function ($ionicPopup) {
window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction;
window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
window.IDBCursor=window.IDBCursor||window.webkitIDBCursor|| window.msIDBCursor;
var db={
dbName: 'aptdb',
dbVersion: 2089, //用小数会四舍五入
dbInstance: {},
errorHandler: function (error) {
console.log('error: ' + error.target.error.message);
},
// 打开数据库连接
open: function (func, fail) {
var dbContent = window.indexedDB.open(db.dbName, db.dbVersion);
// 数据库打开请求的跟新回调函数
dbContent.onupgradeneeded = db.upgrade;
// 数据库打开请求的失败回调函数
dbContent.onerror = db.errorHandler;
// 数据库打开请求的成功回调函数
dbContent.onsuccess = function (e) {
db.dbInstance = dbContent.result;
db.dbInstance.onerror = fail;
func();
};
},
// 数据库版本跟新操作
upgrade: function (e) {
var _db = e.target.result,names = _db.objectStoreNames;
// 此处可以创建多个表
var name = "cart";
if (!names.contains(name)) {
// 创建表
_db.createObjectStore(
name,
{
keyPath: 'goodsId',
autoIncrement: false
});
}
},
// 获取表对象
getObjectStore: function (objectStoreName, mode) {
var txn, store;
mode = mode || 'readonly';
txn = db.dbInstance.transaction([objectStoreName], mode);
store = txn.objectStore(objectStoreName);
return store;
},
// 增加数据方法
add: function (objectStoreName, data, success, fail) {
db.open(function () {
var store, req, mode = 'readwrite';
store = db.getObjectStore(objectStoreName, mode);
req = store.add(data);
req.onsuccess = success;
req.onerror=fail;
},fail);
},
// 修改数据方法
update: function (objectStoreName, data, success, fail) {
db.open(function () {
var store, req, mode = 'readwrite';
store = db.getObjectStore(objectStoreName,mode);
req = store.put(data);
req.onsuccess = success;
req.onerror=fail;
},fail);
},
// 获取全部数据方法
getAll: function (objectStoreName, success, fail) {
db.open(function () {
var store = db.getObjectStore(objectStoreName), cursor = store.openCursor(), data = [];
cursor.onsuccess = function (e) {
var result = e.target.result;
if (result && result !== null) {
data.push(result.value);
result.continue();
} else {
success(data);
}
};
cursor.onerror=fail;
},fail);
},
// 获取单条数据方法
get: function (id, objectStoreName, success, fail) {
db.open(function () {
var store = db.getObjectStore(objectStoreName), req = store.get(id);
req.onsuccess = function (e){
success(e.target.result);
};
req.onerror=fail;
});
},
// delete是保留字
// 删除数据方法
'delete': function (id, objectStoreName, success, fail) {
db.open(function () {
var mode = 'readwrite', store, req;
store = db.getObjectStore(objectStoreName, mode);
req = store.delete(id);
req.onsuccess = success;
req.onerror=fail;
});
},
// 删除表数据方法
deleteAll: function (objectStoreName, success, fail) {
db.open(function () {
var mode, store, req;
mode = 'readwrite';
store = db.getObjectStore(objectStoreName, mode);
req = store.clear();
req.onsuccess = success;
req.onerror=fail;
});
}
};
return db;
}]);

ng-cordova

官网

http://ngcordova.com/docs/

使用步骤

下载 ng-cordova 的js文件

在项目根目录下面打开命令窗口,输入

1
$ bower install ngCordova -save

在index.html文件中引入 ng-cordova.js 文件

在app.js(项目入口中)引入依赖

1
angular.module('myApp', ['ngCordova']);

在 $ionicPlatform.ready 事件中使用插件功能,就是开始使用我们的插件

下载相应功能的插件,按照官方文档使用就可以了

在项目根目录下面打开命令窗口

1
cordova plugin add [插件名称]

比如:

1
cordova plugin add cordova-plugin-camera

调用手机摄像头功能保存头像

调用 actionsheet

1
2
3
4
5
6
7
8
9
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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
// 我的页面
angular.module('account.controller', ['account.service'])
.controller('AccountCtrl', function ($scope, $window, AccountFty, $ionicPopup, $ionicActionSheet, $cordovaCamera) {
// 在每次进入控制器的时候检查 localstorage 是否有头像的数据,如果有,把数据赋值给图片 img 的 src 属性
if (localStorage["touxiang"]) {
var image = document.getElementById('touxiang');
image.src = "data:image/jpeg;base64," + localStorage["touxiang"];
}
// 调用摄像头功能
$scope.func_showAction = function () {
// 显示操作表
$ionicActionSheet.show({
buttons: [
{text: '照相机'},
{text: '图库'}
],
titleText: '请选择文件源',
cancelText: '取消',
buttonClicked: function (index) {
switch (index) {
case 0:
func_getPicFromCamera();
break;
case 1:
func_getPicFromPicture();
break;
}
return true;
}
});
};
// 从摄像头获取图片
var func_getPicFromCamera = function () {
var options = {
quality: 100,
destinationType: Camera.DestinationType.DATA_URL,
sourceType: Camera.PictureSourceType.CAMERA,
allowEdit: true,
encodingType: Camera.EncodingType.JPEG,
targetWidth: 100,
targetHeight: 100,
popoverOptions: CameraPopoverOptions,
saveToPhotoAlbum: false,
correctOrientation: true
};
$cordovaCamera.getPicture(options).then(function (imageData) {
// 获取页面中的img对象
var image = document.getElementById('touxiang');
image.src = "data:image/jpeg;base64," + imageData;
// 保存我们获取的头像数据,下次登录的时候就可以显示了吧
localStorage["touxiang"] = imageData;
}, function (err) {
//$scope.AlertPopup(err);
});
};
// 从图库获取图片
var func_getPicFromPicture = function () {
var options = {
quality: 100,
destinationType: Camera.DestinationType.DATA_URL,
sourceType: Camera.PictureSourceType.PHOTOLIBRARY,
allowEdit: true,
encodingType: Camera.EncodingType.JPEG,
targetWidth: 100,
targetHeight: 100,
popoverOptions: CameraPopoverOptions,
saveToPhotoAlbum: false,
correctOrientation: true
};
$cordovaCamera.getPicture(options).then(function (imageData) {
// 获取页面中的 img 标签
var image = document.getElementById('touxiang');
image.src = "data:image/jpeg;base64," + imageData;
localStorage["touxiang"] = imageData;
}, function (err) {
//$scope.AlertPopup(err);
});
};
// 打电话
$scope.func_callPhone = function (number) {
$window.location.href = "tel:" + number;
};
// 退出方法
$scope.func_exitApp = function () {
var confirmPopup = $ionicPopup.confirm({
title: '提示',
template: "确认退出?"
});
confirmPopup.then(function (res) {
if (res) {
// 退出 app
ionic.Platform.exitApp();
}
});
}
});

注意点:

一定要把 common.css 中的关于 actionsheet 的样式给加载进去,否则样式打包之后会错乱

怎么调用 cordova-camara 插件

http://my.oschina.net/u/1416844/blog/491533

常见问题

常见问题

这个时候你需要运行:

运行

Base64文件介绍

http://www.aimks.com/css-path-data-image-png-usage-base64.html
http://www.zhihu.com/question/36306744/answer/71626823

白名单设置

页面代码

1
<a class="telephone" href="tel:88889999">88889999</a>

设置白名单

http://rickluna.com/wp/2012/02/making-a-phone-call-from-within-phonegap-in-android-and-ios/

app图标

在项目的根目录下创建 resources 文件夹。

在文件夹中都放入 icon.png(应用图标,最小 192x192px,不带圆角),splash.png(启动屏幕,最小 2208x2208px,中间区域 1200x1200px )(可以是 png、psd、ai)

在 cmd 中进入项目所在文件夹执行:必须联网

1
2
ionic resources --icon
ionic resources --splash

项目中的config文件中是这样

1
2
3
4
5
6
7
8
9
10
11
12
13
<platform name="android">
<icon src="resources\android\icon\drawable-ldpi-icon.png" density="ldpi"/>
<icon src="resources\android\icon\drawable-mdpi-icon.png" density="mdpi"/>
<icon src="resources\android\icon\drawable-hdpi-icon.png" density="hdpi"/>
<icon src="resources\android\icon\drawable-xhdpi-icon.png" density="xhdpi"/>
<icon src="resources\android\icon\drawable-xxhdpi-icon.png" density="xxhdpi"/>
<splash src="resources\android\splash\drawable-land-ldpi-screen.png" density="land-ldpi"/>
<splash src="resources\android\splash\drawable-land-mdpi-screen.png" density="land-mdpi"/>
<splash src="resources\android\splash\drawable-port-ldpi-screen.png" density="port-ldpi"/>
<splash src="resources\android\splash\drawable-port-mdpi-screen.png" density="port-mdpi"/>
<splash src="resources\android\splash\drawable-port-hdpi-screen.png" density="port-hdpi"/>
</platform>
<icon src="resources\android\icon\drawable-xhdpi-icon.png"/>

因为在运行的时候要上传图片,所以需要联网不错的插件

双击退出应用

实现思路

$cordovaToast
$ionicPlatform.registerBackButtonAction
$location.path

判断时候是第一次进入应用

其他平台介绍

P++支付平台
极光推送平台
微信开发平台

项目优化

使用gulp压缩源代码

插件扩展

http://git.oschina.net/Cheergoal/BarcodeScanner
http://cordova.apache.org/docs/en/latest/guide/hybrid/plugins/index.html
http://www.iteye.com/blogs/subjects/cordova
http://www.oschina.net/question/2296277_232018
angular,ui-router,indexdb,swiper, 媒体查询,rem,cordova,ng-cordova,Promise/A+
css3, scss, gulp

感谢您的支持!