前回のサンプルではModelとView辺がうまく分離できていなかったので修正してみた
Backbone.Modelに状態を保存し、イベントを拾って、Undoなどの処理を行うようにしてみた
- app.ts
////// /// /// module sample { var undoButton = $("#undo"); var redoButton = $("#redo"); var addButton = $("#add"); var updateNameButton = $("#update_name"); var updateValueButton = $("#update_value"); export class App { private listview: ItemListView; private appModel: AppModel; constructor() { this.appModel = new AppModel(); this.listview = new ItemListView({ "collection":this.appModel.get("collection") }); var self = this; undoButton.click(function () { console.log("undoButton.click"); self.appModel.set({ "undoButtonClicked": true }); }); redoButton.click(function () { console.log("redoButton.click"); self.appModel.set({ "redoButtonClicked": true }); }); addButton.click(function () { console.log("addButton.click"); self.appModel.set({ "addButtonClicked": true }); }); updateNameButton.click(function () { console.log("updateNameButton.click"); self.appModel.set({ "updateNameButtonClicked": true }); }); updateValueButton.click(function () { console.log("updateValueButton.click"); self.appModel.set({ "updateValueButtonClicked": true }); }); } draw(): void { this.listview.render(); } } class AppModel extends Backbone.Model { private undoHandler: UndoHandler; constructor(options?) { super(options); this.undoHandler = new UndoHandler(this.get("collection")); this.bind("change", this.change); } defaults() { return { collection: new ItemCollection([ new ItemModel({ "name": "0", "value": "0" }), ]), undoButtonClicked: false, redoButtonClicked: false, addButtonClicked: false, updateNameButtonClicked: false, updateValueButtonClicked:false } } change(): void { if (this.get("undoButtonClicked")) { this.undoHandler.undo(); this.set({ "undoButtonClicked": false }); } if (this.get("redoButtonClicked")) { this.undoHandler.redo(); this.set({ "redoButtonClicked": false }); } if (this.get("addButtonClicked")) { this.get("collection").add(new ItemModel({ "name": "0", "value": "0" })); this.set({ "addButtonClicked": false }); } if (this.get("updateNameButtonClicked")) { this.get("collection").forEach(function (item) { item.set("name", _.random(100).toString()); }); this.set({ "updateNameButtonClicked": false }); } if (this.get("updateValueButtonClicked")) { this.get("collection").forEach(function (item) { item.set("value", _.random(100).toString()); }); this.set({ "updateValueButtonClicked": false }); } console.log("AppModel.change"); } } class UndoHandler { private manager: Backbone.UndoManager; constructor(collections:Backbone.Collection ) { var self = this; self.manager = new Backbone.UndoManager({ register: collections, track: true }); self.manager.removeUndoType("change:isChanging"); self.manager.addUndoType("change:isChanging", { "on": function (model, isChanging, options) { console.log("manager.addUndoType.on"); }, "undo": function (model, before, after, options) { console.log("manager.addUndoType.undo"); model.set(before) }, "redo": function (model, before, after, options) { console.log("manager.addUndoType.redo"); model.set(after) } }); self.manager.on("all", function (type) { console.log("all.type:" + type); switch (type) { case "undo": { console.log("undo"); break; } case "redo": { console.log("redo"); break; } } }); self.manager.trigger("undo redo"); self.manager.stack.on("add", function () { console.log("stack.on.add"); }); } undo(): void{ this.manager.undo(true); } redo(): void { this.manager.redo(true); } } class ItemModel extends Backbone.Model { constructor(options?) { if (options) { if (options.name) this.set({ "name": options.name }, { "validate": true }); if (options.value) this.set({ "value": options.value }, { "validate": true }); } super(options); this.bind("change", this.change); } change(): void { console.log("ItemModel.change:name="+this.get("name")+",value="+this.get("value")); } defaults() { return { "id": _.uniqueId(), "name": "", "value":"" } } validate(attrs) { if (!attrs.name || _.isEmpty(attrs.name)) { return "name must not be empty"; } if (!attrs.value || _.isEmpty(attrs.value)) { return "value must not be empty"; } } } class ItemCollection extends Backbone.Collection { constructor(options) { super(options); } } class ItemView extends Backbone.View { template: (data: any) => string; constructor(options?) { var html = $("#template-item").html(); this.template = _.template(html); this.events = { "click": "onclick", "change":"onchange" }; super(options); this.model.bind("change", this.render, this); } private onclick() { console.log("ItemView.onclick"); } private change() { console.log("ItemView.onchange"); this.render(); } render(): ItemView { var html = this.template(this.model.toJSON()); $(this.el).html(html); return this; } } class ItemListView extends Backbone.View { constructor(options?) { this.el = "#item-list"; if (options) { if (options.collection) this.collection = options.collection; } super(options); this.listenTo(this.collection, 'add', this.render); this.listenTo(this.collection, 'remove', this.render); this.listenTo(this.collection, 'reset', this.render); } render(): ItemListView { var el = $(this.el); el.empty(); this.collection.forEach(item=> { console.log("ItemListView.render"); var view=new ItemView({ "model": item }).render(); el.append(view.el); }); return this; } } } new sample.App().draw();
- index.html
AppModelクラスが状態を保存している。このBackboneModelに対し、各マウスイベントで発生したClickイベントの関数からModelに対し値をセット。Backboneのchangeイベントが発生するのでそれを拾って処理を行う感じ
Backboneをこのように使うのかどうかは不明。。自己流です