Class.Binds
A common pattern when writing classes in MooTools is to add events to elements or iterate over values. Often this requires you to bind the methods used to the instance of the class with function.bind. Consider this example:
var LinkAlert = new Class({ initialize: function(links){ this.links = $$(links); this.links.each(this.setupLink, this); }, setupLink: function(link) { link.addEvent('click', function(){ this.alert(link); }.bind(this)); }, alert: function(link){ alert(this.prefix + link.get('href')); }, prefix: "The link you clicked goes to " });
This class takes the links you pass it, adds a click event to each and, when the link is clicked, calls it's alert method to tell the user that they clicked the link. Kinda pointless, I know. What I'm trying to illustrate are two very common forms of binding.
The first bind is in the initialize method, which passes each link to the method setupLink. Because setupLink references this we must bind it (which is the second argument that Array.each takes):
$$(links).each(this.setupLink, this);
The other common binding pattern is when you add an event to an Element or a Class. You specify the action (click) and the method to execute when the event is fired (in this case, the alert method). Again, we must bind this to the method, but because addEvent doesn't take a second argument for binding (like Array.each does) we must use the Function.bind method.
link.addEvent('click', function(){ this.alert(link); }.bind(this));
Removing Events
Finally, the last pattern I want to illustrate comes when you want to remove an event. Let's say we want to take our class and have a method to make it stop monitoring our links. There are numerous ways to write this, but let's say we want to remove the events. Because Function.bind returns a copy of the function, we must keep a reference to that new method:
var LinkAlert = new Class({ initialize: function(links){ this.links = $$(links); this.links.each(this.setupLink, this); this.boundAlert = this.alert.bind(this); }, setupLink: function(link) { link.addEvent('click', this.boundAlert); }, alert: function(link){ alert(this.prefix + link.get('href')); }, prefix: "The link you clicked goes to ", stopListening: function(){ this.links.each(function(link) { link.removeEvent('click', this.boundAlert); }, this); } });
By keeping a reference to the version of this.alert that's been bound to this, we are able to remove it. What we couldn't do is this:
link.removeEvent('click', this.alert.bind(this));
Because Function.bind returns a copy of this.alert, which isn't the same thing when you do it again - it's another copy.
Class.Binds
This script, Class.Binds lets you specify methods in your class that should be bound automatically to the instance. Just pass an array of the methods that need this treatment and you no longer have to bind this to them when you reference them. This would yield:
var LinkAlert = new Class({ Implements: Class.Binds, Binds: ["alert", "setupLink"], initialize: function(links){ this.links = $$(links); this.links.each(this.setupLink); }, setupLink: function(link) { link.addEvent('click', this.alert); }, alert: function(link){ alert(this.prefix + link.get('href')); }, prefix: "The link you clicked goes to ", stopListening: function(){ this.links.each(function(link) { link.removeEvent('click', this.alert); }, this); } });
Also note that the binds property can be a single string if you're only binding one method:
var Foo = new Class({ Implements: Class.Binds, Binds: 'foo', initialize: function(){ this.bindMethods(); //this.binds will be used by default } foo: function(){...}, bar: function(){...} })
This is a Class mutator, so you don't need to do anything to use it other than include the script and then define Binds properties for your classes.
cnet-libraries/01.1-class.extras/02-class.binds.txt · Last modified: 2009/01/23 12:18 by aaron-n