如何解决 Backbone 的相同 Model 事件绑定问题
展开全部
我在你的应用场景下做了点测试,在没有修改BackBone任何源代码的情况下实现了你的需求,下面是具体的解决方案:
按照你的需要定义Model,Collection,ItemView,AppView,其中ItemView具体是你需要更新的原子视图,而AppView是应用程序视图;
ItemViewA和ItemViewB应该分布在两个不同的AppView实例中,要做到这点也很容易,因为从业务逻辑上来讲,相同的数据分布在不同的地方可以看成是不同的应用域,上面几个的关系是:
Collection拥有多个Model,Model定义了数据的操作,每个ItemView对应1个Model,而1个AppView对应1个Collection,因为在javascript中的对象是按引用传递的,两个AppView的实例所引用的Collection实际指向相同的对象,这样当AppViewA中的某个ItemViewA触发某种事件,肯定会自动的触发最底层的同1个Model的事件。
这样说来可能有点抽象,我根据官方示例的Todos应用改了改,下面是相关的代码,重点需要注意的部分用黑体字标出,你很快就明白了。
主要的客户端JS代码:
// Todo Model
// Our basic **Todo** model has ` id`, `content`, `done` attributes.
var Todo = Backbone.Model.extend({
// If you don't provide a todo, one will be provided for you.
EMPTY: "empty todo...",
// Ensure that each todo created has `content`.
initialize: function () {
if (!this.get("content")) {
this.set({"content": this.EMPTY});
}
},
// Toggle the `done` state of this todo item.
toggle: function () {
this.save({done: Math.abs(1 - parseInt(this.get("done"))) });
this.change();
},
// Remove this Todo from *database* and delete its view.
clear: function () {
this.destroy();
this.view.remove();
}
});
// Todo Collection
// The collection of todos is backed by *database* instead of a remote
// server.
var TodoList = Backbone.Collection.extend({
// Reference to this collection's model.
model: Todo,
// Save all of the todo items under the `"todos"` namespace.
//localStorage: new Store("todos"),
url: '/todos',
// Filter down the list of all todo items that are finished.
done: function () {
return this.filter(function (todo) {
return parseInt(todo.get('done'));
});
},
// Filter down the list to only todo items that are still not finished.
remaining: function () {
return this.without.apply(this, this.done());
},
// Todos are sorted by their original insertion order.
comparator: function (todo) {
return parseInt(todo.get('id'));
}
});
// Create our global collection of **Todos**.
var Todos = new TodoList();
// Todo Item View
// The DOM element for a todo item...
var TodoView = Backbone.View.extend({
//... is a list tag.
tagName: "li",
// Cache the template function for a single item.
template: _.template($('#item-template').html()),
// The DOM events specific to an item.
events: {
"click .check" : "toggleDone",
"dblclick div.content" : "edit",
"click span.destroy" : "clear",
"keypress .input" : "updateOnEnter"
},
// The TodoView listens for changes to its model, re-rendering.
// Since there's a one-to-one correspondence between a **Todo**
// and a **TodoView** in this app, we set a direct reference
// on the model for convenience.
initialize: function () {
_.bindAll(this, 'render', 'close');
this.model.bind('change', this.render);
this.model.view = this;
},
// Re-render the contents of the todo item.
render: function () {
$(this.el).html(this.template(this.model.toJSON()));
this.setContent();
return this;
},
// To avoid XSS (not that it would be harmful in this particular app),
// we use `jQuery.text` to set the contents of the todo item.
setContent: function () {
this.$('.content').text(this.model.get('content'))
.autotag(this.model.get('tags'), {url: '#/tag/'})
.autolink({text: '(点此)'});
this.$('.created').attr({'title': this.model.get('created')}).prettyDate();
this.input = this.$('.input');
this.input.bind('blur', this.close);
this.input.val(this.model.get('content'));
},
// Toggle the `"done"` state of the model.
toggleDone: function () {
this.model.toggle();
},
// Switch this view into `"editing"` mode, displaying the input field.
edit: function () {
$(this.el).addClass("editing");
this.input.focus();
},
// Close the `"editing"` mode, saving changes to the todo.
close: function () {
this.model.save({content: this.input.val()});
$(this.el).removeClass("editing");
},
// If you hit `enter`, we're through editing the item.
updateOnEnter: function (e) {
if (e.keyCode === 13) {
this.close();
}
},
// Remove this view from the DOM.
remove: function () {
$(this.el).fadeOut('fast', function () {
$(this).remove();
});
},
// Remove the item, destroy the model.
clear: function () {
this.model.clear();
}
});
// The Application
// Our overall **AppView** is the top-level piece of UI.
var AppView = Backbone.View.extend({
// Instead of generating a new element, bind to the existing skeleton of
// the App already present in the HTML.
el: $("#todoapp"),
// Our template for the line of statistics at the bottom of the app.
statsTemplate: _.template($('#stats-template').html()),
// Delegated events for creating new items, and clearing completed ones.
events: {
"keypress .new-todo": "createOnEnter",
"keyup .new-todo": "showTooltip",
"click .clear a": "clearCompleted"
},
// At initialization we bind to the relevant events on the `Todos`
// collection, when items are added or changed. Kick things off by
// loading any preexisting todos that might be saved in *database*.
initialize: function (options) {
_.bindAll(this, 'addOne', 'addAll', 'render');
this.input = this.$(".new-todo");
// 这里需要重新定义AppView的el属性,因为是两个不同的应用
this.el = $(options.element);
Todos.bind('add', this.addOne);
Todos.bind('refresh', this.addAll);
Todos.bind('all', this.render);
Todos.fetch();
},
// Re-rendering the App just means refreshing the statistics -- the rest
// of the app doesn't change.
render: function () {
var done = Todos.done().length;
this.$('div.stats').html(this.statsTemplate({
total: Todos.length,
done: Todos.done().length,
remaining: Todos.remaining().length
}));
},
// Add a single todo item to the list by creating a view for it, and
// appending its element to the `<ul>`.
addOne: function (todo) {
var view = new TodoView({model: todo});
this.$("ul.todos").prepend(view.render().el);
this.$("ul.todos li:first").fadeIn('fast');
},
// Add all items in the **Todos** collection at once.
addAll: function () {
this.$("ul.todos").html('');
Todos.each(this.addOne);
},
// Generate the attributes for a new Todo item.
newAttributes: function () {
return { content: this.input.val() };
},
// If you hit return in the main input field, create new **Todo** model,
// persisting it to *database*.
createOnEnter: function (e) {
if (e.keyCode !== 13) {
return;
}
Todos.create(this.newAttributes());
this.input.val('');
},
// Clear all done todo items, destroying their models.
clearCompleted: function () {
_.each(Todos.done(), function (todo) { todo.clear(); });
return false;
},
// Lazily show the tooltip that tells you to press `enter` to save
// a new todo item, after one second.
showTooltip: function (e) {
var tooltip = this.$(".ui-tooltip-top");
var val = this.input.val();
tooltip.fadeOut();
if (this.tooltipTimeout) {
clearTimeout(this.tooltipTimeout);
}
if (val === '' || val === this.input.attr('placeholder')) {
return false;
}
var show = function () { tooltip.show().fadeIn(); };
this.tooltipTimeout = _.delay(show, 1000);
}
});
// 在两个不同的地方创建两个AppView实例,其中1个App更新之后,另外1个也会随着更新
var App = new AppView({element: '#todoapp'});
var App2 = new AppView({element: '#todoapp2'});
推荐律师服务:
若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询