Backbone.js phần 3

Ở bài trước đã trình bày về phần trung tâm của backbone.js như là Model và Collection. Ở bài viết này sẽ trình bày về phần quyết định kiến trúc của Backbone.js là Backbone.Events

Source code của lần này chi tiết ở đây

Backbone.Events

Backbone.js cung cấp component Model, Collection, View, Router và tất cả đều kế thừa của Backbone.Events(gọi là Events) Events cung cấp các event có thể sử dụng để setting (on, method listenTo) và xoá (off, method stopListening) và trigger, trong các component đó thì có thể kết hợp lại với nhau.

Giải thích cụ thể thêm một chút nữa thì Model, Collection là component biểu hiện data nhưng mà việc control giới hạn hiển thị ở màn hình là việc của view. Ở view, sử dụng Events, thông báo việc thay đổi Model, Collection thông qua event và Model, Collection không cần phải trực tiếp thao tác đến view, phía view sau khi phát sinh các event thì sẽ tiến hành các xử lý cần thiết.

Setting và Xoá Event

Tiêu chuẩn event của Backbone.js được ghi đầy đủ ở tài liệu này Chúng ta cùng đi tìm hiểu setting event thực tế sẽ như thế nào

Phương phá để setting event và xoá bỏ event thông qua 2 cái. Cái thứ nhất là sử dụng on(bind)/off(unbind) Đối với object của phía phát sinh event, chỉ định method callback để thông báo với đối tượng của event

model.on( event, callback, [context] );

Có thể chỉ định tên event như sau ”add”、”change”、”all” ngoài ra, có thể định nghĩa mỗi event map như dưới đây

model.on( {
    event1: callback1,
    event2: callback2,
    event3: callback3
} );

cái thứ 2 là sử dụng listenTo với stopListening đối với obect ở phía setting event thì chỉ định ra method callback thông báo, đối tượng của event với obect phát sinh event

observer.listenTo( target, event, callback );

Điểm khác nhau của 2 phương pháp này là có phải là phía phát sinh event hay là phía setting event.

Quản lý event(add, change, remove, reset, sort)

Vừa xem setting event và cùng xem cách hoạt động thực tế. Sử dụng Model và Collection đã được sử dụng ở bài trước.

var Memo = Backbone.Model.extend({
    idAttribute:"_id",
    defaults:{
    "content":""
},
validate:function (attributes) {
    if (attributes.content === "") {
        return "content must be not empty.";
        }
    }
});
 
var MemoList = Backbone.Collection.extend({
    model:Memo,
    url:"/memo"
});
 
var memoList = new MemoList();

Tạo object để sử dụng cho việc setting event cho Model và Collection đã tạo ở trên

var observer = {
    showArguments:function () {
        console.log("+++observer.showArguments: ");
        _.each(arguments, function (item, index) {
            console.log("  +++arguments[" + index + "]: " + JSON.stringify(item));
        });
    }
};
 
_.extend(observer, Backbone.Events);
 
observer.listenTo(memoList, "all", observer.showArguments);

tạo obecjt observer để setting và định nghĩa method callback showArguments observer object là một tính năng Backbone.Event và nó sẽ kế thừa _extend Ngoài ra, đối với Collection tsetting tất cả event, khi callback thì sẽ gọi method showArguments

Cùng kiểm tra thực tế event sẽ phát sinh như thế nào

var memo = new Memo({content:"Acroquest"});
 
console.log("add");
memoList.add(memo);
 
console.log("change");
memo.set({content:"Acroquest Technology"});
 
console.log("remove");
memoList.remove(memo);
 
console.log("reset");
memoList.add([new Memo({content:"Acro1"}),new Memo({content:"Acro2"}),new Memo({content:"Acro3"})]);
 
console.log("Before reset: " + JSON.stringify(memoList));
 
memoList.reset([new Memo({content:"Acro"}), new Memo({content:"Technology"}), new Memo({content:"Acroquest"})]);
 
console.log("After reset: " + JSON.stringify(memoList));
 
console.log("sort");
 
memoList.comparator = function (item) {
    return item.get("content");
};
memoList.sort();
 
console.log("After sort: " + JSON.stringify(memoList));
 
observer.stopListening();

Kết quả ở console

add
+++observer.showArguments:
  +++arguments[0]: "add"
  +++arguments[1]: {"content":"Acroquest"}
  +++arguments[2]: [{"content":"Acroquest"}]
  +++arguments[3]: {}
 
change
+++observer.showArguments:
  +++arguments[0]: "change:content"
  +++arguments[1]: {"content":"Acroquest Technology"}
  +++arguments[2]: "Acroquest Technology"
  +++arguments[3]: {}
+++observer.showArguments:
  +++arguments[0]: "change"
  +++arguments[1]: {"content":"Acroquest Technology"}
  +++arguments[2]: {}
 
remove
+++observer.showArguments:
  +++arguments[0]: "remove"
  +++arguments[1]: {"content":"Acroquest Technology"}
  +++arguments[2]: []
  +++arguments[3]: {"index":0}
 
reset
 
Before reset: [{"content":"Acro1"},{"content":"Acro2"},{"content":"Acro3"}]
 
+++observer.showArguments:
  +++arguments[0]: "reset"
  +++arguments[1]: [{"content":"Acro"},{"content":"Technology"},{"content":"Acroquest"}]
  +++arguments[2]: {"previousModels":[{"content":"Acro1"},{"content":"Acro2"},{"content":"Acro3"}]}
 
After reset: [{"content":"Acro"},{"content":"Technology"},{"content":"Acroquest"}]
 
sort
+++observer.showArguments:
  +++arguments[0]: "sort"
  +++arguments[1]: [{"content":"Acro"},{"content":"Acroquest"},{"content":"Technology"}]
  +++arguments[2]: {}
 
After sort: [{"content":"Acro"},{"content":"Acroquest"},{"content":"Technology"}]

Quản lý event (request, sync, destroy, invalid)

Quản lý event liên quan đến RESTful JSON interface

console.log("request, sync");
 
memo = new Memo({content:"Murata"}, {collection:memoList});
 
console.log("create");
memo.save(null, {
    success:function () {
        console.log("After create memoList: " + JSON.stringify(memoList));
        console.log("After create memoList.length: " + memoList.length);
    }
}).pipe(function () {
    console.log("fetch");
    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") === "Murata";
    });
 
    console.log("invalid");
    tempMemo.save({content:""});
 
    console.log("invalid wait:true");
    tempMemo.save({content:""}, {wait:true});
 
    console.log("re-save");
 
    return tempMemo.save({content:"Kenichiro"}, {
        success:function () {
            console.log("After save memoList: " + JSON.stringify(memoList));
            console.log("After save memoList.length: " + memoList.length);
        }
    });
}).done(function () {
    console.log("destroy");
 
    var tempMemo = memoList.find(function (item) {
        return item.get("content") === "Kenichiro";
    });
 
    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);

code giống với lần trước. Tuy nhiên có thêm 1 điểm là phần validaion Ở Model có method validation khi xử lý save tuỳ vào method validation mà có validation error hay không Trường hợp phát sinh error thì không tiến hành request lên phía server, và phát hành event invalid trường hợp mà không chỉ định wait:true ở options hash thì sẽ được tiến hành thay đổi property nhưng trường hợp wait:true, check validation mà error thì sẽ không tiến hành thay đổi property

Cùng check kết quả ở console log

request, sync
 
create
+++observer.showArguments:
  +++arguments[0]: "add"
 
After add memo: {"content":"Murata"}
After add memoList.length: 1
 
+++observer.showArguments:
  +++arguments[0]: "change:__v"
+++observer.showArguments:
  +++arguments[0]: "change:date"
+++observer.showArguments:
  +++arguments[0]: "change:_id"
+++observer.showArguments:
  +++arguments[0]: "change"
 
After create memoList: [{"content":"Murata","__v":0,"date":"2013-02-21T13:04:54.036Z","_id":"51261b76ef23b0a066000001"}]
After create memoList.length: 1
 
+++observer.showArguments:
  +++arguments[0]: "sync"
 
fetch
+++observer.showArguments:
  +++arguments[0]: "request"
+++observer.showArguments:
  +++arguments[0]: "reset"
 
After fetch memoList: [{"date":"2013-02-21T13:04:54.036Z","content":"Murata","_id":"51261b76ef23b0a066000001","__v":0}]
After fetch memoList.length: 1
 
+++observer.showArguments:
  +++arguments[0]: "sync"
 
invalid
+++observer.showArguments:
  +++arguments[0]: "change:content"
+++observer.showArguments:
  +++arguments[0]: "change"
+++observer.showArguments:
  +++arguments[0]: "invalid"
  +++arguments[1]: {"date":"2013-02-21T13:04:54.036Z","content":"","_id":"51261b76ef23b0a066000001","__v":0}
  +++arguments[2]: "content must be not empty."
  +++arguments[3]: {"validate":true}
 
invalid wait:true
+++observer.showArguments:
  +++arguments[0]: "invalid"
  +++arguments[1]: {"date":"2013-02-21T13:04:54.036Z","content":"","_id":"51261b76ef23b0a066000001","__v":0}
  +++arguments[2]: "content must be not empty."
  +++arguments[3]: {"validate":true,"wait":true}
 
re-save
+++observer.showArguments:
  +++arguments[0]: "change:content"
+++observer.showArguments:
  +++arguments[0]: "change"
+++observer.showArguments:
  +++arguments[0]: "request"
+++observer.showArguments:
  +++arguments[0]: "change:date"
+++observer.showArguments:
  +++arguments[0]: "change"
 
After save memoList: [{"date":"2013-02-21T13:04:54.309Z","content":"Kenichiro","_id":"51261b76ef23b0a066000001","__v":0}]
After save memoList.length: 1
 
+++observer.showArguments:
  +++arguments[0]: "sync"
 
destroy
+++observer.showArguments:
  +++arguments[0]: "request"
+++observer.showArguments:
  +++arguments[0]: "remove"
+++observer.showArguments:
  +++arguments[0]: "destroy"
 
After destroy memoList: []
After destroy memoList.length: 0

Có thể tổng hợp kết quả ở log trên như sau:

  • trường hợp quản lý event của Collection, Model không có add thì không thể phát hành event của Model
  • change event theo thứ tự change:attribute => change và sau đó phát sinh event sync
  • trường hợp đã fetch thì theo thứ tự request => reset => sync
  • trường hợp gọi save bằng wait:false (default) thì change:content của propery đã chỉ định (content) phát sinh event change, sau đó phía server thay đổi thì nhận change:date => change
  • trường hợp destroy, thứ tự sẽ là request => remove => destroy

Kết Luận

Ở bài viết này là lần thứ ba đã trình bày về Backbone.Events Ở bài viết tiếp theo sẽ trình bày tiếp phần Backbone.View