Intro
OOP is a great concept, it simplifies the programmer’s life, especially in the field of interface development. Nearly all modern web-interfaces use JS in some way – for field highlighting, validation, help etc. It’s OK when there are such simple things.
And now imagine, that we have a set of dialogue windows (HTML+CSS) for adding a comment, rating, sending to the friend. Each of these boxes has a number of functions – check if user is logged in(if not – show login form), send AJAX request, process response, show error/success messages, position the box on the page etc.
The problem
First idea I had about them is to use simple inheritance, create Box class, derive CommentBox, RatingBox and other classes, but it turned out, that jQuery (I used it there, because system is built using Yii framework, which uses jQuery for client-side coding) doesn’t support extending classes like, for example, ExtJS or YUI. Yes, it has $.extend() function, but it just copies the object, not actually extends class. Why it is not the same? Let’s look here:
-
//namespace
-
var Popups = {};
-
//class constructor
-
Popups.Box = function()
-
{
-
//initialization here
-
}
-
Popups.Box.prototype = {
-
container:$(‘<div class="container"></div>’),
-
content:null,
-
create:function()
-
{
-
//dom elements generation here
-
},
-
showHide:function()
-
{
-
//some logic here
-
}
-
//other methods and properties go here
-
}
Normally (in ExtJS, for example) when writing:
-
Popups.CommentBox = $.extend(true,{}, Popups.Box, {
-
create:function()
-
{
-
alert(‘overriden!’);
-
}
-
})
I expect, that if I do:
-
var c = new CommentBox();
-
c.create();
I should get an alert window. However, this is not true for jQuery. It just copies everything and we get some messed things. So I gave this up and followed another pattern.
The solution
Box object became container for all boxes. I took care of messaging, determining if user is logged, and all other routines. It also serves as a factory, because all clicking icons go to the showHide method and it has to determine, which box to show and create this inner object.
And this inner object takes care of the box-specific logic. If it needs some common info, for example, the ID of the product it is called for, it calls the Box method (see LowCoupling GRASP principle here
). So in the Box I have:
-
getBoxType:function()
-
{
-
//determine the box we’re dealing with by the link class
-
var tg = $(this.event.target);
-
if (tg.hasClass(‘f’))
-
return ‘favourite’;
-
else if (tg.hasClass(‘t’))
-
return ‘friend’;
-
else if (tg.hasClass(‘ra’))
-
return ‘rate’;
-
else if (tg.hasClass(‘c’))
-
return ‘comment’;
-
else if (tg.hasClass(‘r’))
-
return ‘rss’;
-
else if (tg.hasClass(‘add’))
-
return ‘addTag’;
-
else if (tg.hasClass(‘b’))
-
return ‘bookmark’;
-
return ”;
-
},
-
getInnerObj:function()
-
{
-
if (!us.logged)//if user is not logged, we show him the login box instead of anything else
-
{
-
return new Popups.LoginObject(this);
-
}
-
var cls;
-
this.type = this.getBoxType();
-
switch (this.type)
-
{
-
case ‘favourite’:
-
cls = this.objects[this.type] = new Popups.FavouriteObject(this);
-
break;
-
case ‘friend’:
-
cls = this.objects[this.type] = new Popups.FriendObject(this);
-
break;
-
case ‘rate’:
-
cls = this.objects[this.type] = new Popups.RateObject(this);
-
break;
-
case ‘comment’:
-
cls = this.objects[this.type] = new Popups.CommentObject(this);
-
break;
-
case ‘rss’:
-
cls = this.objects[this.type] = new Popups.RssObject(this);
-
break;
-
case ‘bookmark’:
-
cls = this.objects[this.type] = new Popups.BookmarkObject(this);
-
break;
-
default:
-
cls = this.objects[this.type] = null;
-
}
-
return cls;
-
},
-
show:function()
-
{
-
//some stuff
-
var obj = this.getInnerObj();
-
if (typeof obj == ‘object’ && obj != null)
-
{
-
this.content.append(obj.getContents());
-
}
-
},
So instead of inheritance I implemented aggregation (Box aggregates CommmentObject, Favourite object etc).
Everything goes well until you want to to attach some method of current object as a handler to some event. For example:
-
Popups.Box.prototype = {
-
//properties here
-
productID:0,
-
-
create:function()
-
{
-
this.form.submit(this.onSubmit);
-
productID = this.parentBox.getItemID();
-
},
-
onSubmit:function()
-
{
-
alert(this.productID);
-
}
-
//other methods follow
-
}
Right? Wrong! Because when submit event occurs, jQuery will call the onSubmit method, but this will refer to the form, not to the Box object! However, it is logical to put processing of the form into the same object. In ExtJS and YUI you can pass the scope parameter for every callback function, so you can control the “this” variable in the callback call. jQuery doesn’t allow this. I used the following workaround:
-
Popups.Box.prototype = {
-
//properties here
-
productID:0,
-
-
create:function()
-
{
-
var ctx = this;
-
this.form.submit(function(event){
-
ctx.onSubmit(event);
-
});
-
productID = this.parentBox.getItemID();
-
},
-
onSubmit:function(event)
-
{
-
alert(this.productID);
-
}
-
//other methods follow
-
}
If you need the object, that triggered an event, just pass “this” there:
-
create:function()
-
{
-
var ctx = this;
-
this.form.submit(function(event){
-
ctx.onSubmit(this, event);
-
});
-
productID = this.parentBox.getItemID();
-
},
-
onSubmit:function(form, event)
-
-
alert(this.productID);
-
}
Having these 2 tricks you can use OOP approach to designing reusable interface components in a handy modern way.
Back to inheritance
Since I discovered the problem with inheritance, I want to develop a plugin, that fixes this, that will provide nice class inheritance model to take jQuery a step ahead. I was going to digg the ExtJS’s Ext.extend() code and find out how this is done correctly, but while preparing an article, I found this material, which is actually a guide for creation of such plugin: http://phrogz.net/JS/Classes/OOPinJS2.html. Unfortunately, I don’t have time for this now, but I plan to do this in the nearest future. If someone does this earlier, please let me know
Links
- ExtJS – a component, interface-oriented JS framework
- YUI – Yahoo library for the user interface. ExtJS was derived from it long ago. They are similar in extending and event processing (the “scope” I mentioned initially appeared in YUI)
- jQuery – a lightweight VERY popular JS framework with an easily extensible structure, but lacks OOP techniques!
No related posts.
Related posts brought to you by Yet Another Related Posts Plugin.
Share this post with a friend











Zecc says:
You’re doing it wrong. It’s easy to be confused my JavaScript’s somewhat complex object model.
Instead of
Popups.CommentBox = $.extend(true,{}, Popups.Box, { /* … */ });
you should do
Popups.CommentBox.prototype = $.extend(true, {}, new Popups.Box(), { /* … */ });
You’ll even get
January 31, 2010, 19:32box = new Popups.CommentBox();
if( box instance Popup.CommentBox && box instanceof Popups.Box ) alert(‘You betcha!’);
Konstantin Mirin says:
This makes sense. I was developing this quite long ago and can’t remember now why I decided to create new object rather than put all properties and methods to the prototype.
March 11, 2010, 22:22To all readers – try this new proposed approach first. If you experience problems with it, you can always use mine.
And I’d appreciate if you post results of your implementation here
casino en ligne says:
casino en ligne…
[...] Le tarif moyen de l’Hôtel Casino Trump Marina est seulement 137 $ la nuit ce qui en fait un des hôtels les moins chers d’Atlantic City. [...]…
May 23, 2010, 22:59Speed up windows 7 says:
Speed up windows 7…
I think this is wonderful I truly appreciate the informations shared in this post I am going to bookmark this!…
June 6, 2010, 05:15location automobile says:
Resources like the one you mentioned here will be very useful to me! I will post a link to this page on my blog. I am sure my visitors will find that very useful.
location automobile
July 4, 2010, 16:55jouer au casino says:
cool thanks fo this tuto!
July 12, 2010, 17:22zqrfzhyl says:
zqrfzhyl…
zqrfzhyl…
July 27, 2010, 12:53meilleurs casinos says:
nice tuto thanks you !
July 27, 2010, 17:47pmu says:
great tuto, thanks
July 28, 2010, 17:31winamax says:
very nice tuto
August 2, 2010, 19:32apparel says:
i always face this problem now i solve this matter thanks
August 8, 2010, 05:16winamax says:
Super news, bon article, je follow!
August 9, 2010, 13:08bonus betclic says:
thanks for the article
August 9, 2010, 18:06gagner says:
very nice tuto
August 9, 2010, 23:13Voyance gratuite says:
very good tuto
August 12, 2010, 07:24paris en ligne says:
very interesting tuto
August 16, 2010, 14:42casino mac says:
this tuto is good thanks!
August 21, 2010, 16:55Betclic says:
it’s a very interesting blog thanks
August 23, 2010, 05:41jouer casino says:
your blog is good i really like it thanks!
August 30, 2010, 18:34france poker en ligne says:
some interesting stuff here i like it!
August 30, 2010, 18:36pronostic sportif says:
i really like your blog thanks!
September 2, 2010, 18:22roulette en ligne says:
this tuto is good thanks!
September 2, 2010, 18:23wheelchair lift says:
Very good advice. I plan to implement this into my own programming.
September 3, 2010, 04:22