本讲解案例于nodejs框架编写的服务端和ionic与angular编写的前端,说明$Resource 服务和服务端是如何打交道的
功能介绍:通过Resource服务和服务器端打交道,获取数据库中的相关数据
数据库使用mongodb
1.ionic angularjs
1.1 ionic前端 H5和js代码
前端代码展现的是俩个select单选框,
<div class="list">
<label class="item item-input item-select" data-tap-disabled="true">
<div class="input-label">
酒窖编号
</div>
<select ng-model="selectedCellar" ng-options="cellar.name for cellar in cellars" ng-change="selectCellarChange(selectedCellar)"></select>
</label>
<label class="item item-input item-select" data-tap-disabled="true">
<div class="input-label">
区域编号
</div>
<select ng-model="selectedArea" ng-options="area.areaname for area in areas" ng-change="selectAreaChange(selectedArea)"></select>
</label>
</div>
1.2 controller
控制器中设置了下拉刷新,下拉刷新的同时使用Cellars自定义service向服务器请求相关数据。
$scope.doRefresh = function () {
$scope.cellars = undefined;//清空已选择记录
$scope.areas = undefined;
//下面的方法就是调用iotservices中的方法
Cellars.query(function (cellars) {
$scope.$broadcast('scroll.refreshComplete');
if (cellars.error) {
$window.alert('Retrieve cellars list failed!');
return;//以下不执行
}
$scope.cellars = cellars;//正确就执行赋值语句,将请求到的cellars赋值出来。
console.log(cellars);
}, function () {
$scope.$broadcast('scroll.refreshComplete');
$window.alert('Retrieve device list failed!');
});
};
$scope.areas = [];
$scope.selectCellarChange = function (selectedCellar) {
Areas.query({ "name": selectedCellar.name }, function (areas) {
if (areas.error) {
$window.alert('Retrieve cellars list failed!');
return;
}
$scope.areas = areas;
console.log(areas);
}, function () {
$window.alert('Retrieve device list failed!');
});
}
1.3 angularjs自定义服务
这里使用了$resource这个服务,可以创建一个资源对象Cellars,可以把Cellars对象理解成同RESTful的后端服务进行交互的接口,在控制器中Cellars.query就是使用了这个资源对象
angular.module('starter.iotservices', [])
.factory('Cellars', ['$resource', '$http', '$window','Settings', function ($resource, $http, Settings) {
var Server = Settings.httpServer;
return $resource('api/admin/cellar/:id', { id: '@cellarid' },
{
})
}])
.factory('Areas', ['$resource', '$http', '$window', 'Settings', function ($resource, $http, Settings) {
var Server = Settings.httpServer;
return $resource('api/admin/area?name=:name', { name: '@cellarname' },
{
});
}])
这里我就可以先介绍一下$Resource
1.3.1 get
表达式不定义具体的参数,get()请求一般被用来获取单个资源。
get(params,successFn,errrorFn)
//GET /api/users nodejs服务器中get请求的编程方式
Cellars.get(function(resp){
//处理成功
},function(err){
//处理错误
});
如果参数中传入了具名参数(例子中的参数是id),那么get()方法会向包含id的URL(这个在nodejs服务端中)发送请求:
//向nodejs服务器routes发起一个请求:GET-->/api/users/123
User.get({id:'1'},function(resp){
//success
},function(error){
//fail
});
1.3.2 query 请求:
query向指定URL发送一个GET请求,并期望返回一个JSON格式的资源对象集合。
query()和get()方法之间唯一的区别是AngularJS期望query()方法返回数组。
//发起一个请求,上面的控制器就是这个
Cellars.query(function(cellars){
//读取集合中的所有对象
$scope cellars=cellars;
});
1.3.3 save 请求:
save(params, payload, successFn, errorFn)
save方法向指定URL发送一个POST请求,并用数据体来生成请求体。save()方法用来在服务器上生成一个新的资源。 payload:代表请求发送的数据体.下面是创建一个酒窖区域Area的后台处理方法,数据体就是用户定义的酒窖区域name,
//发送一个请求 with the body { "areaname": $scope.area.areaname }
$scope.createArea = function (area) {
if (!$scope.area.areaname) {
$window.alert('area name is required!');
return;
}
Areas.save({ name: $scope.cur_wc }, { "areaname": $scope.area.areaname }, function (resp) {
if (resp.err) {
$window.alert(resp.err);
return;
}
$scope.hideModal();
$scope.actionwc.push(resp);
}, function () {
$window.alert('Create area failed! Please try again later.');
});
};
1.3.4 delete 请求:
请求处理方式如下
delete(params, payload, successFn, errorFn)
delete方法会向指定URL发送一个DELETE请求,并用数据体来生成请求体。它被用来在服务器上删除一个实例:
// DELETE /api/users
User.delete({}, {
id: '123'//删除id为123的数据体
}, function(response) {
// 处理成功的删除响应
}, function(response) {
// 处理非成功的删除响应
});
1.3.5 remove 请求:
请求处理方式如下,这个东西是在nodejs中编写的
remove方法和delete()方法的作用是完全相同的,它存在的意义是因为delete是JavaScript的保留字,在IE浏览器中会导致额外的问题。
remove(params, payload, successFn, errorFn)
// 发起一个请求:
// DELETE /api/users
User.remove({}, {
id: '123' //删除id为123的使用者
}, function(response) {
// 处理成功的删除响应
}, function(response) {
// 处理非成功的删除响应
});
2. 实例解释:通过resource服务进行酒窖和区域的save和delete操作
2.1 post请求创建酒窖cellars:
2.1.1前端:
<form ng-submit="createWc()" class="form-horizontal">
<div class="form-group">
<label class="col-sm-4 control-label">酒窖名称
</label>
<div class="col-sm-6">
<input ng-model="winecell.name" type="text" class="form-control" required placeholder="Winecell Name">
</div>
</div>
<div class="form-group">
<div class="col-sm-6 col-sm-offset-4">
<div class="btn-group pull-right">
<button type="submit" class="btn btn-primary">创建</button>
<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
2.1.2控制器处理,用到post方法:
$scope.createWc = function () {
if (!$scope.winecell.name) {
$window.alert('Device name is required!');
return;//下面将不会被执行
}
if (Winecells.post({ "name": $scope.winecell.name }))
{
$scope.hideModal();
}
else $window.alert('Create device failed! Please try again later.');
};
2.1.3 后台service.js进行处理,连接到api进行处理,Winecells是自定义服务,$resource注入的angular服务:
angular.module('iotgo').
factory('Winecells', ['$resource','$http','$window', function ($resource,$http) {
return $resource('/api/admin/cellar/:id', {id:'@cellarid'},
{
post:{method:'POST'}
});
如果参数中传入了具名参数(我们例子中的参数是id),那么post()方法会向包含id的URL发送请求,注意上面代码中的”@cellarid”,使用了@后,当执行post时,向/api/admin/cellar/:cellarid路由发送请求。
2.1 .4 mongodb数据库中的配置 和 后台api路由处理cellars:
*mongodb数据库中的配置
nodejs db文件夹下
index.js文件
exports.Cellar = Cellar;//路径是db/index,在api中用到的
var Cellar = require('./cellar');//
var Area = require('./area');
var mongoose = require('mongoose');
cellar.js文件
这就是和本地mongodb数据库的交互操作了
var mongoose = require('mongoose');
var uuid = require('uuid');
var Schema = mongoose.Schema;
var now = function () {
return new Date();
};
var incCellarid = function(cellarid){
var cellarid = (parseInt(cellarid.substr(0), 16) + 1).toString(16);
if (cellarid.length > 3) {
return false;
}
if (cellarid.length < 3) {
cellarid = '000'.substr(cellarid.length) + cellarid;
}
return cellarid;
};
// Exports
var schema = new Schema({
name: { type: String, unique: true },
cellarid: { type: String, require: true, index: true, match: /^[0-9a-f]{3}/ },
apikey: { type: String, unique: true, default: uuid.v4 },
createdAt: { type: Date, index: true, default: now }
});
schema.static('exists', function(name, callback){
this.where('name', name).findOne(callback);
});
schema.static('getNextCellarId', function(name, callback){
this.where('name').select('cellarid').sort('-cellarid').findOne(function(err, cellar){
if(err) {
callback(err);
return;
}
var cellarid;
if(! cellar) {
cellarid = "001";
callback(null, cellarid);
return;
}
cellarid = incCellarid(cellar.cellarid);
if(! cellarid){
callback('Not enough device ids available!');
return;
}
callback(null, cellarid);
});
});
schema.static('getCellarByCellarid', function(cellarid, callback){
this.where('cellarid', cellarid).findOne(function(err, cellar){
if(err) {
callback(err);
return;
}
callback(null, cellar);
});
});
module.exports = mongoose.model('Cellar', schema);
* api 对数据库调用的操作在这里getCellarByCellarid函数在db,cellar.js中定义
/api/admin/cellar//:cellarid
var express = require('express');
var db = require('../../db/index');//请求数据库了
var Cellar = db.Cellar;
.post(function(req, res){
Cellar.getCellarByCellarid(req.params.cellarid, function(err, cellar){
if(err || ! cellar) {
res.send({ err: "Cellar dose not exist." });
return;
}
cellar.name = req.body.name;
cellar.save(function(err, cellar){
if(err){
res.send({ err: "Save cellar failed." });
return;
}
res.send(cellar);
});
});
})