關於angular的$resource中的isArray屬性問題

詩&遠方發表於2014-10-09

在之前的文章中講到了在使用$resource的時候,有一個isArray屬性. 這個屬性在兩個地方有提到:

1. angular學習筆記(二十八)-$http(6)-使用ngResource模組構建RESTful架構

$resource的四個方法: query方法,get方法,save方法,remove方法,delete方法

這四個方法裡,唯獨query方法,它的isArray屬性是true,所以,query接受到的資料是陣列,而其餘四個方法,接收到的資料必須不能是陣列

2. angular學習筆記(二十八-附1)-$resource中的資源的方法

$resource的自定義方法: 比如該篇的栗子中使用的$charge方法,在定義自定義方法的時候,也要指定isArray屬性.不指定的話預設是false.

 

如果我們不設定isArray為true,但是後臺卻給它返回一個陣列資源,是會報錯的.

那麼,如果在使用get方法,save方法,remove方法,delete方法的時候,需要返回陣列物件呢? 答案是不能的.因為$resource這個服務沒有提供相應的配置方法.

那麼,如果自定義一個方法,設定isArray為true,是不是就可以接受陣列格式的返回值了呢? 答案是,要看情況! 

比如下面這段程式碼:

var notePad = angular.module('notePad',['ngResource']);
notePad.factory('myNotes',['$resource',function($resource){
    return $resource('/notes/:id',{id:'@id'},{mysave:{method:'POST',isArray:true}})
}]);
notePad.factory('loadNotes',['myNotes','$q',function(myNotes,$q){
    return function(){
        var defer = $q.defer();
        myNotes.query(function(notes){
            defer.resolve(notes);
        },function(err){
            defer.reject(err)
        });
        return defer.promise
    }
}]);
notePad.factory('loadNote',['myNotes','$q',function(myNotes,$q){
    return function(noteId){
        var defer = $q.defer();
        myNotes.get({id:noteId},function(note){
            defer.resolve(note);
        },function(err){
            defer.reject(err)
        });
        return defer.promise
    }
}]);
notePad.directive('notepad',['loadNotes','myNotes',function(loadNotes,myNotes){
    return {
        restrict:'EA',
        templateUrl:'template.html',
        scope:{},
        link:function(scope,iEle,iAttr){
            scope.editMode = false;
            scope.curText = '';
            scope.ifNew = true;
            loadNotes().then(function(data){scope.notes=data},function(data){});
            var editBox =  iEle.find('.edit');
            editBox.bind('keydown keyup',function(){
                scope.curText = $(this).html()
            });
            scope.editNote = function(id){
                scope.editMode = true;
                if(id != undefined){
                    scope.curText = scope.notes[id]['content']
                }
                else {
                    scope.curText = '';
                }
            };
            scope.saveNote = function(){
                scope.editMode = false;
                if(scope.ifNew){
                    var newNote = new myNotes();
                    newNote.content = scope.curText;
                    newNote.title = scope.curText.length>5 ? scope.curText.substring(0,5)+'...' : scope.curText;
                    newNote.id = scope.notes.length;
                    newNote.$mysave(function(data){console.log(data)})
                }
            }
        }
    }
}]);

內容比較多,只看關鍵部分程式碼:

1.myNotes服務通過$resource建立$resource()物件,然後給它新增自定義的mysave方法,設定isArray屬性為false.

2.例項化一個newNote資源,然後呼叫自定義的$mysave方法

後臺我們給它返回一個陣列物件:

var notes = [{
    'title':'吃飯',
    'content':'吃飯啊',
    'id':0
},{
    'title':'睡覺',
    'content':'睡覺啊',
    'id':1
},{
    'title':'喂兔子',
    'content':'喂兔子啊',
    'id':2
}];
app.post('/notes/:id',function(req,res){
    var noteId = req.params.id;
    notes[noteId] = req.body;
    res.send(notes)
});

結果報錯: 

這個很好理解,因為我們設定了isArray是false嘛.

然後我把isArray屬性改為true:

結果還是報錯:

百思不得其解後去看了angular-resource的原始碼,發現裡面有這樣一段:

var isInstanceCall = this instanceof Resource;
var value = isInstanceCall ? data : (action.isArray ? [] : new Resource(data));
if (action.isArray) {
                  value.length = 0;
                  forEach(data, function (item) {
                    if (typeof item === "object") {
                      value.push(new Resource(item));
                    } else {
                      // Valid JSON values may be string literals, and these should not be converted
                      // into objects. These items will not have access to the Resource prototype
                      // methods, but unfortunately there
                      value.push(item);
                    }
                  });
                }

注意這裡的value物件,並非設定isArray為true時,它就是[],首先要求isInstanceCall是false,然後再判斷isArray...

所以,這裡的newNote是$resource()的例項,這樣,isArray無論如何配置,它都不能接受陣列格式的返回資料.並且必須配置為false.

所以這裡應該這樣做:


newNote.$mysave(function(data){console.log(data)})

//改為:
myNotes.mysave(newNote,function(data){console.log(data)});

不要使用資源例項的方法,而是直接使用$resource()的方法.這樣就可以正確接收陣列格式的返回值了.

angular這樣設計,應該是符合RESTful架構風的(我猜的),因為提交一個資源,那麼返回的也應該是一個資源,而不應該是整個陣列,所以應該儘量避免這種做法.這裡只是我自己做練習的時候遇到這樣的情況.

完整程式碼參考: angular指令實戰-notepad

 

相關文章