ngResource模塊是angular專門為RESTful架構而設計的一個模塊,它提供了'$resource'模塊,$resource模塊是基於$http的一個封裝.下面來看看它的詳細用法
1.引入angular-resource.min.js文件
2.在模塊中依賴ngResourece,在服務中注入$resource
var HttpREST = angular.module('HttpREST',['ngResource']);
HttpREST.factory('cardResource',function($resource){ return $resource('/card/user/:userID/:id',{userID:123,id:'@id'},{charge:{method:'POST',params:{charge:true},isArray:false}}) });
3.$resource的參數:
$resource(url,{url參數},{自定義方法})
url: 必填,資源的基礎url
url中帶有 ':' 項的是根據第二個參數來進行配置的.
url參數: 選填,配置url中的帶有 ':' 項的參數
eg:
('/card/user/:userID/:id',{userID:123,id:'@id'}),那么userID會被配置為123.
另外,在調用$resource()的方法的時候(比如get,query...),可以傳入參數覆蓋這里對url參數的配置,這在后面說得到它的方法的時候再詳解
而id屬性在后面講第三個參數的時候講解
自定義方法:
使用$resource獲取到的資源,或者通過$resource實例化的資源,資源本身會具有一些方法,比如$save,第三個參數用於給資源添加自定義的方法:詳見:http://www.cnblogs.com/liulangmao/p/3907032.html
4.$resource()的方法:
$resource()一共有以下5個方法:
get:
{method:'GET'}
一般用於獲取某個資源:
query:
{method:'GET',isArray:true}
一般用於獲取一整套的資源,以數組形式返回
save:
{method:'POST'}
一般用於保存某個資源,有可能是新建的資源,也有可能是更新現有的資源
remove:
{method:'DELETE'}
一般用於刪除某個資源
delete:
{method:'DELETE'}
一般用於刪除某個資源
eg:
1.首先通過$resource創建一個服務:
var HttpREST = angular.module('HttpREST',['ngResource']); HttpREST.factory('cardResource',function($resource){ return $resource('/card/user/:userID/:id',{userID:123,id:'@id'},{charge:{method:'POST',params:{charge:true},isArray:false}}) });
這個cardResource服務,返回的是一個對象,對象有get,query,save,remove,delete五個方法
2.然后我們通過cardResource這個服務,來創建另外一個獲取資源的服務:
HttpREST.factory('httpCard',function($q,cardResource){ return { getById:function(cardID){ var defer = $q.defer(); cardResource.get({id:cardID},function(data,headers){ defer.resolve(data); },function(data,headers){ defer.reject(data); }); return defer.promise }, query:function(){ var defer = $q.defer(); cardResource.query(function(data,headers){ defer.resolve(data); },function(data,headers){ defer.reject(data); }); return defer.promise } } });
httpCard這個服務返回的對象有兩個方法,一個getById方法,用於通過id加載資源,一個query方法,用於獲取全部的資源.
然后來講解一下get方法和query方法的用法:
get和query方法都是GET類型的請求,他們的調用方式是相同的:
cardResource.action([parameters], [success], [error])
[parameters]: 可選. 一個json對象,用於配置url里的參數,比如這里寫了{id:cardID},那么提交的請求url就是 '/card/user/123/cardID'.
可以不填,不填就直接按照$resource()里的url來提交,注意,不填的話,不需要給個空,可以直接寫success回調,angular能夠判斷出它沒有填第一個參數,而不是死板的按照順序來解讀參數.
[success]:可選. 請求成功后的回調函數.回調接受2個參數(注意這里和$http有所不同):
function(data,headers){
//data是請求到的內容
//headers是響應頭
}
[error]:可選. 請求失敗后的回調.回調接受1個參數
function(httpResponse){
//httpResponse暫不知道是什么. 反正是和響應有關
}
凡是通過$resource返回的對象,一定是json格式的,如果后台返回的數據不是json,$resource也會按照自己的方式處理成json格式,比如后台返回字符串'我愛你',那么如果是get方法,得到的數據就是:
{
0:我,
1:愛,
2:你
}
而query方法定義了isArray為true,所以他的返回值必須是數組,並且數組里的每個值都必須是json格式的對象.如果返回的是字符串'我愛你',那么如果是query方法,得到的數據就是:
[
{0:'我'},
{0:'愛'},
{0:'你'}
]
如果返回的是一個json對象{name:'code_bunny'},那么得到的數據就是:
[{name:'code_bunny'}]
所以,在后台最好就做好相應的處理,按照規范格式返回數據
3. 在控制器中使用httpCard服務來獲取資源:
HttpREST.controller('Card',function($scope,httpCard,cardResource){ //通過id獲取銀行卡 $scope.card_1 = httpCard.getById(1); $scope.card_2 = httpCard.getById(2); $scope.card_3 = httpCard.getById(3); //獲取所有的銀行卡 $scope.cards = httpCard.query(); });
<span>{{card_1['name']}}</span> <span>{{card_1['amount']}}</span> <br/> <span>{{card_2['name']}}</span> <span>{{card_2['amount']}}</span> <br/> <span>{{card_3['name']}}</span> <span>{{card_3['amount']}}</span> <br/>
node:
var cards = [ { id:1, name:'建設銀行', amount:0 }, { id:2, name:'中國銀行', amount:0 }, { id:3, name:'上海銀行', amount:0 } ]; app.get('/card/user/123/:id',function(req,res){ var data = cards[req.params.id-1]; setTimeout(function(){res.send(data)},2000) }); app.get('/card/user/123',function(req,res){ res.send(cards) });
關於card_1...cards明明是promise對象,但視圖中卻正確顯示了資源的問題,請參考:http://www.cnblogs.com/liulangmao/p/3907307.html
4. 在控制器中使用cardResource服務的save方法來更新資源:
HttpREST.controller('Card',function($scope,httpCard,cardResource){ $scope.card_3 = httpCard.getById(3); //更新id為3的銀行卡 $scope.updataCard = function(){ $scope.card_3.then(function(data){ data.name='工商銀行'; cardResource.save(data); //data.$save() }); }; });
<button ng-click="updataCard()">更新id為3的銀行卡</button>
<br/>
<span>{{card_3['name']}}</span> <span>{{card_3['amount']}}</span>
node:(這段nodejs同時處理了charge和save)
var url = require('url');
app.post('/card/user/123/:id',function(req,res){ var index = req.params.id-1; var query = url.parse(req.url,true)['query']; if (query.charge){ cards[index]['amount']+= Number(query['amount']) } else { cards[index] = req.body; } res.send(cards[index]); });
點擊后→
cardResource.save(data) 和 data.$save()在這里,兩者是等價的.但是在有參數的時候,他們接受的參數其實是不同的:
使用cardResource.save([parameters], postData, [success], [error])方法時,可以接受四個參數:
[parameters]: 可選.用於配置url參數,比如配置{userID:124},那么請求url就會變成 'card/user/124/3',其中的3,還是從請求體的id屬性獲取的.
同樣,如果沒有參數需要配置,是不要填空的.不存在順序一一對應.可以直接把postData作為第一個參數.
postData: 必填. 發送的請求體. save方法是post請求,必須要帶有請求體.
[success]:選填. 響應成功后的回調函數,參數同get方法成功回調里的參數
[error]:選填. 響應失敗后的回調函數.參數同get方法失敗回調里的參數
使用data.$save([parameters], [success], [error])方法時,可以接受三個參數:
[parameters]: 可選.注意它不是用於配置url的參數的.它是用來設置url?后面的參數的! 比如設置{name:'code_bunny'},那么請求url就會變成'card/user/123/3?name=code_bunny',
通過$save方法來調用save方法,是不能夠配置url參數的.它直接就是提交資源自己.
同樣,如果沒有參數需要配置,是不要填空的.不存在順序一一對應.可以直接把[success]作為第一個參數.
[success]:選填. 響應成功后的回調函數,參數同get方法成功回調里的參數
[error]:選填. 響應失敗后的回調函數.參數同get方法失敗回調里的參數
至於為什么要在then回調里處理,為什么save后視圖會自動更新,請查看:http://www.cnblogs.com/liulangmao/p/3907307.html 以及 http://www.cnblogs.com/liulangmao/p/3907032.html
5. 在控制器中使用cardResource服務的save方法來新建資源:
(1)新建帶有id的資源:
//添加id為4的銀行卡 $scope.addCard4 = function(){ var card_4 = new cardResource(); card_4['id'] = 4; card_4['name'] = '浦發銀行'; card_4['amount'] = 0; card_4.$save(function(data){$scope.card_4=data});
//cardResource.save(card_4,function(data){$scope.card_4=data}); };
<span>{{card_4['name']}}</span> <span>{{card_4['amount']}}</span>
node:(這段nodejs同時處理了charge和save)
app.post('/card/user/123/:id',function(req,res){ var index = req.params.id-1; var query = url.parse(req.url,true)['query']; if (query.charge){ cards[index]['amount']+= Number(query['amount']) } else { cards[index] = req.body; } res.send(cards[index]); });
點擊后→
新建資源需要通過new cardResource(), 這樣它就是$resource()的實例,就擁有了$save,$charge等資源的方法:
card_4.$save(function(data){$scope.card_4=data});
cardResource.save(card_4,function(data){$scope.card_4=data});
上面已經說過了,這兩種寫法自然是等價的.但這里的回調里需要給$scope_card_4進行賦值,因為原來的$scope下是沒有card_4這個變量的.
(2)新建沒有id的資源:
//添加沒有id的銀行卡 $scope.addCard = function(){ var newCard = new cardResource(); newCard['name'] = '農業銀行'; newCard['amount'] = 0; newCard.$save(function(data){$scope.card_5=data});
cardResource.save(newCard,function(data){$scope.card_5=data});
//newCard.$save(function(data){$scope.card_5=data});
};
<button ng-click="addCard()">添加一張不指定id的銀行卡</button> <br/> <span>{{card_5['name']}}</span> <span>{{card_5['amount']}}</span>
node:
app.post('/card/user/123',function(req,res){ var index = cards.length; cards[index] = req.body; res.send(cards[index]); });
點擊后→
cardResource.save(newCard,function(data){$scope.card_5=data});
newCard.$save(function(data){$scope.card_5=data});
上面已經說過了,這兩種寫法自然是等價的.但這里的回調里需要給$scope_card_5進行賦值,因為原來的$scope下是沒有card_5這個變量的.
(1)和(2)的區別在於:
資源是否有id,他們請求的路徑是不同的.沒有id的資源會請求'card/user/123',而有id的資源會請求'card/user/123/id',這在node里的處理是不同的.
在$resource中,基本url里面的 :id 這類通過參數指定的值,如果沒有傳入參數,那么它提交的路徑里就不會包含這一項,比如:
return $resource('/card/user/:userID/:id',{userID:123,id:'@id'})
當使用query方法時,是沒有id值的,get請求的路徑就是/card/user/123
但是在node里面不是這樣的:
app.get('/card/user/123/:id',function(req,res){ //這個只能處理帶有id值的get請求,沒有id請求不能被匹配到處理 }); app.get('/card/user/123',function(req,res){ //這個用來處理沒有id的get請求 }); app.post('/card/user/123/:id',function(req,res){ //這個只能處理帶有id值的post請求,沒有id請求不能被匹配到處理 }); app.post('/card/user/123',function(req,res){ //這個用來處理沒有id的post請求 });
所以,處理有id的資源的post請求和處理沒有id的資源的post請求,在node里需要寫不同的匹配規則,在這里,對於沒有id的資源,我們僅作模擬,就把它按照順序保存在數組最后,然后當然還是把它返回給客戶端.
6. 在控制器中使用cardResource服務的自定義的charge方法來給資源進行充值操作:
//id為1的建設銀行卡增加100元 $scope.addCharge = function(){ $scope.card_1.then(function(card){ card.$charge({amount:100}); }) }
<button ng-click="addCharge()">給建設銀行的卡充值100元</button> <br/> <span>{{card_1['name']}}</span> <span>{{card_1['amount']}}</span>
node:(這段nodejs同時處理了charge和save)
app.post('/card/user/123/:id',function(req,res){ var index = req.params.id-1; var query = url.parse(req.url,true)['query']; if (query.charge){ cards[index]['amount']+= Number(query['amount']) } else { cards[index] = req.body; } res.send(cards[index]); });
這里的charge充值方法需要帶有請求參數,?amount=100,所以它不能使用cardResource.charge([params],data,[success],[error])這種方式來請求,只能通過調用資源自身的$charge方法來進行請求.
card.$charge({amount:100})相當於請求了url: 'card/user/123/1?charge=true&amount=100'
即使是RESTful架構,不表示就不可以在url中帶有參數,帶有參數不影響express對路徑的匹配.獲取路徑參數的方法也就是使用url模塊一樣獲取.
無論POST請求還是GET請求,都可以帶有參數,也都可以獲取參數.
這里對'/card/user/123/:id'這個url的請求做了判斷,獲取它的參數,然后判斷參數里的charge是否等於true,是的話表示充值操作.不是的話表示更新資源操作.分別對資源進行處理,最后都是把操作過的資源返回給客戶端.
其中,url.parse(req.url,true),如果不帶有true,不會把參數解析成json格式.
點擊后→
7. 在控制器中使用cardResource服務的delete方法來刪除資源:
//刪除id為1的銀行卡 $scope.delCard1 = function(){ $scope.card_1.then(function(card){ card.$delete(); }) }
<button ng-click="delCard1()">將id為1的銀行卡刪掉</button> <br/> <span>{{card_1['name']}}</span> <span>{{card_1['amount']}}</span>
node:
app.delete('/card/user/123/:id',function(req,res){ var index = req.params.id-1; cards[index] = null; res.send({}) });
點擊后→
這里有兩點需要注意:
1.card.$delete()不等同於cardResource.delete(card),因為它這里是'DELETE'方式的請求,不是POST方式的請求,當我們使用card.$delete()的時候,它是把card作為請求體發送給后台的(相當於POST請求體).但是當我們使用cardResource.delete(card)的時候,它是把card對象解析成url參數一起傳給后台的(相當於GET請求里url后面的參數).也就是說,card.$delete()請求的url是'card/user/123/1',附帶請求體為card. 而cardResource.delete(card)請求的url是'card/user/123/1?amount=0&name=建設銀行'.
還有很重要的一點,其實請求url是'card/user/123/1?amount=0&name=建設銀行'在這個例子中並不會改變后台的處理和返回,但是對於angular來說,card.$delete()會使用返回值去填充card,更新視圖,但是cardResource.delete(card),它並非請求體就是card,所以返回值不會去填充card,不會更新視圖.所以,在使用delete方法時,應該直接使用$delete,而不是delete.(remove亦然)
2.刪除以后,cards[0]就變成空的null,但是不能返回null,因為$resource必須返回json格式,所以要返回{}
完整代碼地址:https://github.com/OOP-Code-Bunny/angular/tree/master/OREILLY/18.6%20%24http(ngResource)