Giới Thiệu Backbone.js phần 2

Trong bài viết lần trước đã giải thích cách sử dụng cụ thể của Model và Collection. Bài viết lần này sẽ tiếp tục giải thích về cách sử dụng tính năng tuyệt vời của Backbone.js đó là bất đồng bộ RESTful JSON interface. Source code của lần này có thể tham khảo ở link ở đâyở đây

RESTful Interface của Backbone.js

Backbone.js lấy tiêu chuẩn dựa vào RESTful JSON interface sử dụng ajax. Cụ thể là sử dụng method save, fetch, destroy của class Model, method create, fetch của class Collection, có thể lưu trữ dữ liệu ở phía server và có thể tiến hành đồng bộ dữ liệu của phía server. Hãy cùng nhau tìm hiểu cách hoạt động thực tế.

Bổ sung

Để có thể thử tính năng này thì cần có app có đủ các thao tác Restful ở phía server. Đã chuẩn bị sẵn Memo application có đầy đủ CRUD đơn giản (Node.js + MongoDB) và sẽ dùng app Memo này để giải thích trong bài viết này.

Tính đọc lập dữ liệu của model Model không được giữ ở Collection mà object Model được giữ riêng lẻ.

Trước tiên, hãy cùng định nghĩa Model, hãy xem code như ở dưới đây

var Memo = Backbone.Model.extend({
    urlRoot: "/memo",
    idAttribute: "_id",
    defaults: {
        "content": ""
    }
});

Thuộc tính idAttribute chỉ định id định danh, phía server sử dụng MongoDB có định danh id nên là sẽ muốn sử dụng _id này thì sẽ định nghĩa như trên. Thuộc tính urlRoot chỉ định path.

Có các method save, fetch, destroy là những method thao tác liên quan đến data ở model. Xử lý HTTP method với URL đã chỉ định ở thuộc tính urlRoot như ở dưới đây. Tuỳ thuộc thuộc tính urlRoot với id của object Model, URL request đến phía server được quyết định. Xử lý này chính là xử lý bất đồng bộ với ajax.

Bảng khái lược về RESTful của Backbone.js

HTTP Method URL Xử lý Method đối ứng
GET /memo get list không chỉ định id sẽ fetch của Model hoặc fetch của Collection
POST /memo create new không chỉ định id và method save của Model hoặc create của Collection
GET /memo/:id show chỉ định id và method fetch của Model
PUT /memo/:id Update chỉ định id và method save của Model
DELETE /memo/:id Delete chỉ định id và method destroy của Model

Và tạo thử instance và xem xử lý lưu trữ data như thế nào

 
console.log("Before save: " + JSON.stringify(memo));
console.log("isNew(): " + memo.isNew());
 
memo.save({content: "Acroquest"}, {
    success: function() {
        console.log("After save(post) memo: " + JSON.stringify(memo));
        console.log("After save(post) memo.isNew(): " + memo.isNew());
    }
});
 
console.log("After save: " + JSON.stringify(memo));
console.log("isNew(): " + memo.isNew());

trường hợp sinh ra memo object thì thông thường id không được setting (isNew() ở trạng thái true) ở trạng thái này gọi method save thì theo bảng ở trên thì sẽ gửi request HTTP POST với URL /memo đến server method save được thực thi không đồng bộ và method save option thứ 2 là gọi call back success, có thể confirm được kết quả được lưu ở phía server. Nhìn vào console log thì có thể hiểu được xử lý

kết quả console log

Before save: {"content":""}
isNew(): true
 
After save: {"content":"Acroquest"}
isNew(): true
 
After save(post) memo: {"content":"Acroquest","__v":0,"date":"2013-02-16T14:00:55.158Z","_id":"511f9117e32557404b000001"}
After save(post) memo.isNew(): false

Có thể thấy thứ tự code đã viết ở trên với kết quả ở console log có xử lý bất đồng bộ. Ngoài ra đến thời điểm gọi success callback thì ở object memo ở phía client chưa thấy setting thuộc tính content nhưng mà ở thời điểm gọi call back success thì thuộc tính id, date đã được setting và được phản ánh ở phía server và kết quả của method isNew() sẽ trả về false vì thuộc tính id đã được setting

Tiếp theo, cùng get data được lưu ở phía server, trạng thái id được setting (isNew() được trả về false) bằng cách gọi method fetch, lúc đó sẽ gửi request GET đến server.

// để kiểm tra  nên thay đổi giá trị
memo.set({content: "Acro"});
 
console.log("Befroe fetch memo: " + JSON.stringify(memo));
 
// サーバ側のデータを同期する
memo.fetch({
    success: function() {
        console.log("After fecth memo: " + JSON.stringify(memo));
    }
});

Kết quả console log

Befroe fetch memo: {"content":"Acro","__v":0,"date":"2013-02-16T15:11:33.534Z","_id":"511fa1a55871772e4c000001"}
 
After fecth memo: {"content":"Acroquest","__v":0,"date":"2013-02-16T15:11:33.534Z","_id":"511fa1a55871772e4c000001"}

Bây giờ cùng thử thay đổi content của object memo (ở trạng thái id được setting) theo như bảng ở phía trên sẽ gọi method save và sẽ request HTTP PUT với URL /memo/;id tới phía server

console.log("Befroe save(put) memo: " + JSON.stringify(memo));
 
memo.save({content: "Acroquest Technology"}, {
    success: function() {
        console.log("After save(put) memo: " + JSON.stringify(memo));
    }
});

Kết quả console log

Befroe save(put) memo: {"content":"Acroquest","__v":0,"date":"2013-02-16T15:20:22.535Z","_id":"511fa3b6377656564c000001"}
 
After save(put) memo: {"content":"Acroquest Technology","__v":0,"date":"2013-02-16T15:20:22.801Z","_id":"511fa3b6377656564c000001"}

Tiếp theo, cùng thử xoá object memo (ở trạng thía id được setting, isNew trả về false), theo như bảng ở phía trên sẽ gọi hàm destroy, và gửi request HTTP DELETE với URL /memo/:id đến phía server.

console.log("Before delete memo: " + JSON.stringify(memo));
 
memo.destroy({
    success: function() {
        console.log("After delete memo: " + JSON.stringify(memo));
    }
});

Kết quả console log

Before delete memo: {"content":"Acroquest Technology","__v":0,"date":"2013-02-16T15:20:22.801Z","_id":"511fa3b6377656564c000001"}
 
After delete memo: {"content":"Acroquest Technology","__v":0,"date":"2013-02-16T15:20:22.801Z","_id":"511fa3b6377656564c000001"}

Theo như xử lý này thì data phía server sẽ bị xoá nhưng mà nhìn vào kết quả console log thì object memo không được xoá ở phía client (Sẽ trình bày sau nhưng mà trường hợp Model đang được lưu ở Collection thì sẽ bị xoá từ Collection)

Tính bền vững của Model với Collection

Model object thông thường sẽ được lưu ở Collection. Phần này sẽ giải thích về method liên quan như là create với fetch của Collection Đầu tiên, cùng định nghĩa Collection lưu trữ Model theo code dưới đây

var Memo = Backbone.Model.extend({
idAttribute: "_id",
  defaults: {
      "content": ""
    }
});
 
var MemoList = Backbone.Collection.extend({
    model: Memo,
    url: "/memo"
});

Trường hợp urlRoot định nghĩa ở Model được lưu vào Collection thì phía Collection không cần định nghĩa url. Và ở Collection định nghĩa thuộc tính model định nghĩa model lưu ở Collection.

Cùng tạo thử Model object

var memoList = new MemoList();
 
console.log("Before collection.length: " + memoList.length)
 
var memo = memoList.create({content: "Acro1"}, {
    success: function() {
        console.log("After create model: " + JSON.stringify(memoList));
        console.log("After create collection.length: " + memoList.length)
    }
});
 
console.log("After model: " + JSON.stringify(memo))
console.log("After collection.length: " + memoList.length)

Kết quả console log

Initial memoList.length: 0
 
After memo: {"content":"Acro1"}
After memoList.length: 1
 
After create memoList: [{"content":"Acro1","__v":0,"date":"2013-02-18T16:54:57.437Z","_id":"51225ce1995ed7e453000001"}]
After create memoList.length: 1

Method create của Collection Sẽ thêm thuộc tính Model vào Collection và lưu ở phía server, 1 điểm cần chú ý đó là, giá trị back của method create không phải là object Deferred mà là Model object. Trường hợp muốn sử dụng Defrred Object thì tương tự ở lần tiếp the khi sinh ra Model object được liên kết với collection thì khi save bằng method save thì sẽ add thuộc tính bằng method add.

var memo = new Memo({content: "Acro2"}, {collection: memoList});
 
memo.save(null, {
    success: function() {
        console.log("After create memoList: " + JSON.stringify(memoList));
        console.log("After create memoList.length: " + memoList.length);
    }
});
 
memoList.add(memo);
 
console.log("After add memo: " + JSON.stringify(memo));
console.log("After add memoList.length: " + memoList.length);

Kết quả console log

Initial memoList.length: 0
 
    After add memo: {"content":"Acro2"}
    After add memoList.length: 1
 
    After save memoList: [{"content":"Acro2","__v":0,"date":"2013-02-18T17:58:01.327Z","_id":"51226ba9f888a3e254000001"}]
    After save memoList.length: 1

Tiếp theo là fetch, giống với Model, hãy tham khảo code bên dưới.

memoList.fetch({
    success: function() {
        console.log("After fetch memoList: " + JSON.stringify(memoList));
        console.log("After fetch memoList.length: " + memoList.length);
    }
});

Khi xoá hay là thay đổi giá trị của Model đã lưu ở Collection, như đã giải thích ở trước thì sử dụng metho save, detroy của Model, hãy tham khảo code dứoi đây.

var memo = new Memo({content: "Acro2"}, {collection: memoList});
 
memo.save(null, {
    success: function() {
        console.log("After save memoList: " + JSON.stringify(memoList));
        console.log("After save memoList.length: " + memoList.length);
    }
}).pipe(function() {
    return memoList.fetch({
        success: function() {
            console.log("After fetch memoList: " + JSON.stringify(memoList));
            console.log("After fetch memoList.length: " + memoList.length);
        }
    });
}).pipe(function() {
    var tempMemo = memoList.find(function(item){ return item.get("content") === "Acro2"; });
 
    return tempMemo.save({content: "Acro3"}, {
        success: function() {
            console.log("After save memoList: " + JSON.stringify(memoList));
            console.log("After save memoList.length: " + memoList.length);
        }
    });
}).done(function() {
    var tempMemo = memoList.find(function(item){ return item.get("content") === "Acro3"; });
 
    return tempMemo.destroy({
        success: function() {
            console.log("After destroy memoList: " + JSON.stringify(memoList));
            console.log("After destroy memoList.length: " + memoList.length);
        }
    });
});
 
memoList.add(memo);
 
console.log("After add memo: " + JSON.stringify(memo));
console.log("After add memoList.length: " + memoList.length);

Kết quả console log

Initial memoList.length: 0
 
  POST http://localhost:3000/memo
 
After memo: {"content":"Acro2"}
After memoList.length: 1
 
After save memoList: [{"content":"Acro2","__v":0,"date":"2013-02-18T18:19:43.493Z","_id":"512270bf82a8845e55000001"}]
After save memoList.length: 1
 
  GET http://localhost:3000/memo
 
After fetch memoList: [{"date":"2013-02-18T18:19:43.493Z","content":"Acro2","_id":"512270bf82a8845e55000001","__v":0}]
After fetch memoList.length: 1
 
  PUT http://localhost:3000/memo/512270bf82a8845e55000001
 
After save memoList: [{"date":"2013-02-18T18:19:43.731Z","content":"Acro3","_id":"512270bf82a8845e55000001","__v":0}]
After save memoList.length: 1
 
  DELETE http://localhost:3000/memo/512270bf82a8845e55000001
 
After destroy memoList: []
After destroy memoList.length: 0

Trong bài viết lần thứ 2 này, đã giải thích về RESTful của Model với Collection Ở bài viết tiếp theo sẽ tiếp tục trình bày về Backbone.Event.

Tham khảo: https://appkitbox.com/knowledge/javascript/20130604-52