Here are a couple of possibilities.
1. Centralized: store ClosedTune
objects in the TuneBook
instance
Store a reference to each ClosedTune
in tune_book.tunes
. How you populate tune_book.tunes
is up to you; since you mentioned an adder method on TuneBook
, that's what I've illustrated below.
In the TuneBook
event handler, retrieve the ClosedTune
from tune_book.tunes
by using something like the id
attribute of the event target as the key. Then call the Tune
or ClosedTune
handler.
http://jsfiddle.net/p5QMT/1/
var Tune = Backbone.View.extend({
className: "tune",
click_handler: function (event) {
event.preventDefault();
console.log(this.id + " clicked");
},
render: function () {
this.$el.html(
'<a href="" class="button">' + this.id + '</a>'
);
return this;
}
});
var ClosedTune = Tune.extend({});
var OpenTune = Tune.extend({
events: {
"click .button" : 'click_handler'
}
});
var TuneBook = Backbone.View.extend({
events: {
"click .tune .button" : 'click_handler'
},
click_handler: function (event) {
var tune = this.options.tunes[
$(event.target).closest(".tune").attr('id')
];
tune.click_handler( event );
},
add_tune: function (tune) {
this.options.tunes[tune.id] = tune;
this.$el.append(tune.render().el);
},
render: function () {
$("body").append(this.el);
return this;
}
});
var tune_book = new TuneBook({
tunes: {}
});
[1, 2, 3].forEach(function (number) {
tune_book.add_tune(new ClosedTune({
id: "closed-tune-" + number
}));
});
tune_book.render();
var open_tune = new OpenTune({
id: "open-tune-1"
});
$("body").append(open_tune.render().el);
2. Decentralized: associate the view object with the DOM object using jQuery.data()
When you create a ClosedTune
, store a reference to it, e.g. this.$el.data('view_object', this)
.
In the event listener, retrieve the ClosedTune
, e.g. $(event.target).data('view_object')
.
You can use the same exact handler for ClosedTune
(in TuneBook
) and OpenTune
, if you want.
http://jsfiddle.net/jQZNF/1/
var Tune = Backbone.View.extend({
className: "tune",
initialize: function (options) {
this.$el.data('view_object', this);
},
click_handler: function (event) {
event.preventDefault();
var tune =
$(event.target).closest(".tune").data('view_object');
console.log(tune.id + " clicked");
},
render: function () {
this.$el.html(
'<a href="" class="button">' + this.id + '</a>'
);
return this;
}
});
var ClosedTune = Tune.extend({
initialize: function (options) {
this.constructor.__super__.initialize.call(this, options);
}
});
var OpenTune = Tune.extend({
events: {
"click .button" : 'click_handler'
}
});
var TuneBook = Backbone.View.extend({
events: {
"click .tune .button": Tune.prototype.click_handler
},
add_tune: function (tune) {
this.$el.append(tune.render().el);
},
render: function () {
$("body").append(this.el);
return this;
}
});
var tune_book = new TuneBook({
tunes: {}
});
[1, 2, 3].forEach(function (number) {
tune_book.add_tune(new ClosedTune({
id: "closed-tune-" + number
}));
});
tune_book.render();
var open_tune = new OpenTune({
id: "open-tune-1"
});
$("body").append(open_tune.render().el);
Response to comment
I considered option 1 but decided against it as I already have a collection of tune models in the tunebook and didn't want another object I'd need to keep in sync
I guess it depends what kind of housekeeping / syncing you feel the need to do, and why.
(e.g. in TuneModel.remove() I would need to remove the view from tunebook's list of views... would probably need events to do this, so an event only solution starts to look more attractive).
Why do you feel that you "need to remove the view from tunebook's list of views"? (I'm not suggesting you shouldn't, just asking why you want to.) Since you do, how do you think @ggozad's approach differs in that respect?
Both techniques store ClosedTune
objects in the TuneBook
instance. In @ggozad's technique it's just hidden behind an abstraction that perhaps makes it less obvious to you.
In my example they're stored in a plain JS object (tune_book.tunes
). In @ggozad's they're stored in the _callbacks
structure used by Backbone.Events
.
Adding a ClosedTune
:
1.
this.options.tunes[tune.id] = tune;
2.
this.on('buttonClick:' + tune.id, tune.handler, tune);
If you want to get rid of a ClosedTune
(say you remove it from the document with tune.remove()
and you want the view object gone completely), using @ggozad's approach will leave an orphaned reference to the ClosedTune
in tune_book._callbacks
unless you perform the same kind of housekeeping that would make sense with the approach I suggested:
1.
delete this.options.tunes[tune.id];
tune.remove();
2.
this.off("buttonClick:" + tune.id);
tune.remove();
The first line of each example is optional -- depending if you want to clean up the ClosedTune
objects or not.
Option 2 is more or less what I'm doing right now, but (for other reasons) I also store the model as a data attribute on view.$el, and I can't help feeling that there's got to be a better way than storing references all over the place.
Well, it ultimately comes down to your preference for how to structure things. If you prefer storing the view objects in a more centralized fashion, you can store them in the TuneBook
instance instead of using jQuery.data
. See #1: Centralized.
One way or another you're storing references to the ClosedTune
objects: using jQuery.data
, or in a plain object in the TuneBook
, or in _callbacks
in the TuneBook
.
If you like @ggozad's approach for reasons that you understand, go for it, but it's not magic. As it's presented here I'm not sure what advantage is supposed to be provided by the extra level of abstraction compared to the more straightforward version I present in #1. If there is some advantage, feel free to fill me in.
Tune
why you want to make the detourChildView -> ParentView -> Model
.. you can execute theModel.handler
directly from theChildView
.. In other hand you can use theModel
as the messenger: you can modify the state of the Model from the ChildView and let the ParentView to listen to this modification. – EnuguParentView -> [childView] -> childModel
– Np