1 Star2 Stars3 Stars4 Stars5 Stars (No Ratings Yet)
Loading ... Loading ...

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:

  1. //namespace
  2. var Popups = {};
  3. //class constructor
  4. Popups.Box = function()
  5. {
  6.         //initialization here
  7. }
  8. Popups.Box.prototype = {
  9.         container:$(‘<div class="container"></div>’),
  10.         content:null,
  11.         create:function()
  12.         {
  13.                 //dom elements generation here
  14.         },
  15.         showHide:function()
  16.         {
  17.                 //some logic here
  18.         }
  19.         //other methods and properties go here
  20. }

Normally (in ExtJS, for example) when writing:

  1. Popups.CommentBox = $.extend(true,{}, Popups.Box, {
  2.         create:function()
  3.         {
  4.                 alert(‘overriden!’);
  5.         }
  6. })

I expect, that if I do:

  1. var c = new CommentBox();
  2. 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:

  1.         getBoxType:function()
  2.         {
  3.                 //determine the box we’re dealing with by the link class
  4.                 var tg = $(this.event.target);
  5.                 if (tg.hasClass(‘f’))
  6.                         return ‘favourite’;
  7.                 else if (tg.hasClass(‘t’))
  8.                         return ‘friend’;
  9.                 else if (tg.hasClass(‘ra’))
  10.                         return ‘rate’;
  11.                 else if (tg.hasClass(‘c’))
  12.                         return ‘comment’;
  13.                 else if (tg.hasClass(‘r’))
  14.                         return ‘rss’;
  15.                 else if (tg.hasClass(‘add’))
  16.                         return ‘addTag’;
  17.                 else if (tg.hasClass(‘b’))
  18.                         return ‘bookmark’;
  19.                 return ;
  20.         },
  21.         getInnerObj:function()
  22.         {
  23.                 if (!us.logged)//if user is not logged, we show him the login box instead of anything else
  24.                 {
  25.                         return new Popups.LoginObject(this);
  26.                 }
  27.                 var cls;
  28.                 this.type = this.getBoxType();
  29.                 switch (this.type)
  30.                 {
  31.                         case ‘favourite’:
  32.                                         cls = this.objects[this.type] = new Popups.FavouriteObject(this);
  33.                                 break;
  34.                         case ‘friend’:
  35.                                         cls = this.objects[this.type] = new Popups.FriendObject(this);
  36.                                 break;
  37.                         case ‘rate’:
  38.                                         cls = this.objects[this.type] = new Popups.RateObject(this);
  39.                                 break;
  40.                         case ‘comment’:
  41.                                         cls = this.objects[this.type] = new Popups.CommentObject(this);
  42.                                 break;
  43.                         case ‘rss’:
  44.                                         cls = this.objects[this.type] = new Popups.RssObject(this);
  45.                                 break;
  46.                         case ‘bookmark’:
  47.                                         cls = this.objects[this.type] = new Popups.BookmarkObject(this);
  48.                                 break;
  49.                         default:
  50.                                 cls = this.objects[this.type] = null;
  51.                 }
  52.                 return cls;
  53.         },
  54.         show:function()
  55.         {
  56.                 //some stuff
  57.                 var obj = this.getInnerObj();
  58.                 if (typeof obj == ‘object’ && obj != null)
  59.                 {
  60.                         this.content.append(obj.getContents());
  61.                 }
  62.         },

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:

  1. Popups.Box.prototype = {
  2.         //properties here
  3.         productID:0,
  4.  
  5.         create:function()
  6.         {
  7.                 this.form.submit(this.onSubmit);
  8.                 productID = this.parentBox.getItemID();
  9.         },
  10.         onSubmit:function()
  11.         {
  12.                 alert(this.productID);
  13.         }
  14. //other methods follow
  15. }

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:

  1. Popups.Box.prototype = {
  2.         //properties here
  3.         productID:0,
  4.  
  5.         create:function()
  6.         {
  7.                 var ctx = this;
  8.                 this.form.submit(function(event){
  9.                         ctx.onSubmit(event);
  10.                 });
  11.                 productID = this.parentBox.getItemID();
  12.         },
  13.         onSubmit:function(event)
  14.         {
  15.                 alert(this.productID);
  16.         }
  17. //other methods follow
  18. }

If you need the object, that triggered an event, just pass “this” there:

  1. create:function()
  2. {
  3.         var ctx = this;
  4.         this.form.submit(function(event){
  5.                 ctx.onSubmit(this, event);
  6.         });
  7.         productID = this.parentBox.getItemID();
  8. },
  9. onSubmit:function(form, event)
  10.  
  11.         alert(this.productID);
  12. }

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 Share this post with a friend

23 Comments

  1. 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
    box = new Popups.CommentBox();
    if( box instance Popup.CommentBox && box instanceof Popups.Box ) alert(‘You betcha!’);

  2. 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.
    To 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 :)

  3. 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. [...]…

  4. Speed 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!…

  5. location 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

  6. jouer au casino says:

    cool thanks fo this tuto!

  7. zqrfzhyl says:

    zqrfzhyl…

    zqrfzhyl…

  8. meilleurs casinos says:

    nice tuto thanks you !

  9. pmu says:

    great tuto, thanks

  10. winamax says:

    very nice tuto

  11. apparel says:

    i always face this problem now i solve this matter thanks

  12. winamax says:

    Super news, bon article, je follow!

  13. bonus betclic says:

    thanks for the article

  14. gagner says:

    very nice tuto

  15. paris en ligne says:

    very interesting tuto

  16. casino mac says:

    this tuto is good thanks!

  17. Betclic says:

    it’s a very interesting blog thanks

  18. jouer casino says:

    your blog is good i really like it thanks!

  19. france poker en ligne says:

    some interesting stuff here i like it!

  20. pronostic sportif says:

    i really like your blog thanks!

  21. roulette en ligne says:

    this tuto is good thanks!

  22. wheelchair lift says:

    Very good advice. I plan to implement this into my own programming.

Leave a Reply