Dojo inheritance, overriding & extending

Dojo is a decent JavaScript framework offering a lot of functionality. One of its biggest trumphs is the possibility to extend modules in an easy way through mixin inheritance.
In this tutorial I’m going to show you how you can extend a widget with extra functionality.

All your base are belong to us

To use inheritance, there are in fact two important modules that will help you with it, namely dojo/_base/declare and dojo/_base/lang. The difference between the two is that you can use the  declare-module to really extend Dojo modules, while with the  lang-module you will extend normal JavaScript objects (both the object as the prototype).

dojo/_base/lang

The lang-module has two functions ahat will help you to extend modules, namely mixin() and extend().
However, these two functions have a fundamental difference, with the extend() function you will apply your extensions to the prototype (similar to a “class”), while using mixin() you will rather extend the object itself.

An example:

require(["dojo/_base/lang"], function(lang) {
   var str = new String("g00glen00b");
    lang.mixin(str, {
        reverse: function() {
           return this.split('').reverse().join('');
        }
    });

    console.log(str.reverse());

    str = new String("g00glen00b");
    console.log(str.reverse());
});

In this example I made a reverse() function and added it to the str object. The first console.log will obviously output b00nelg00g. However, when I create a new object, the reverse() function no longer works.
The reason for this is that with the mixin() function the reverse() function is applied to the first object, but not to the second object.

lang mixin

Let’s try something similar with lang.extend():

require(["dojo/_base/lang"], function(lang) {
   var str = new String("g00glen00b");
    lang.extend(String, {
        reverse: function() {
           return this.split('').reverse().join('');
        }
    });

    console.log(str.reverse());

    str = new String("g00glen00b");
    console.log(str.reverse());
});

In contrary to the mixin() function, the extend() function applies it to a class. That’s why we’re using String as a paremter here.
So now the reverse() function is applied to the String class itself, which means it will work for each String object and then it will work for all strings.
Both console.log(); statements will print the reversed string.

lang extend

So, because these functions are applied directly to the JavaScript object or prototype, means you can use them for all kind of things including Dojo widgets (Dijits).
For example, if you want to extend dijit/form/MultiSelect with sorting capabilities, you could do it using the lang.extend() function:

lang.extend(MultiSelect, {
    sort: function() {
        var domNodes = Array.prototype.slice.call(this.containerNode.children);
        domNodes.sort(function(a, b) {
            if (a.innerHTML < b.innerHTML) {
                return -1;
            } else if (a.innerHTML == b.innerHTML) {
                 return 0;   
            } else {
                return 1;
            }
        });
        this.containerNode.innerHTML = "";
        array.forEach(domNodes, function(node) {
           this.containerNode.appendChild(node); 
        }, this);
    }
});

If you then create a MultiSelect widget:

<select id="fruit" data-dojo-type="dijit/form/MultiSelect">
    <option value="LE" selected>Lemon</option>
    <option value="BA">Banana</option>
    <option value="AP">Apple</option>
    <option value="LI">Lime</option>
    <option value="GR">Grapes</option>
    <option value="PI">Pineapple</option>
    <option value="ME">Melon</option>
</select>

and the sorting functionality will work:

ready(function() {
   registry.byId("fruit").sort(); 
});

multiselect-lang

So, with the dojo/_base/lang module you can already extend modules on a simple way. What you can’t do is inheriting from a module or create submodules based upon it.

For example, in the previous example there is no way to create a MultiSelect without the sorting capabilities.

dojo/_base/declare

A module that can do that is the declare-module. With the declare-module you can easily write you own widgets by extending already existing widgets, mixins and other APIs.

The entire system of inheriting is used extensively in the Dojo framework. Most widgets extend other widgets and/or mixins.
For example, all widgets in Dojo extend from dijit/_WidgetBase at a certain point (can be several levels in the hierarchy lower).

The difference between a widget and a mixin is that a mixin offers certain functionality, but is not being able to stand on its own.
Most widgets with a template will, for example inherit from dijit/_TemplatedMixin which adds a lot of templating functionality to your own widget, but it’s not useful to create instances of dijit/_TemplatedMixin by yourself.
Another common mixin is the dijit/form/_FormMixin for form widgets, … .

If we go back to the previous code example and use the sortable, MultiSelect, we can easily convert this to an example using the declare-module:

 declare("dijit/form/SortableMultiSelect", [MultiSelect], {
    sort: function() {
        var domNodes = Array.prototype.slice.call(this.containerNode.children);
        domNodes.sort(function(a, b) {
            if (a.innerHTML < b.innerHTML) {
                return -1;
            } else if (a.innerHTML == b.innerHTML) {
                return 0;   
            } else {
                return 1;
            }
        });
        this.containerNode.innerHTML = "";
        array.forEach(domNodes, function(node) {
            this.containerNode.appendChild(node); 
        }, this);
    }
});

In this example, the MultiSelect will no longer be sortable, but we did create a custom widget with the name dijit/form/SortableMultiSelect that is sortable.
The only thing that rests us to do is change the data-dojo-type attribute because we’re now working with another widget.

mutliselect-declare

The advantage is that we can still keep the current MultiSelect as the SortableMultiSelect.

Overriding

When you’re creating custom modules, you will at one point certainly have to override already existing features.
You can do that as well with the declare-module, just add a function to your module with the same name but a different implementation and it’s done!

If you still want to call the original function, you can do that from the overriding function by calling:

this.inherited(arguments);

This is is similar to calling super in Java.
If you for example want to create a new DateTextBox that only allows you to select a date in the future, then you will have to override the postCreate() function and add some constraints after initializing the widget.

require(["dojo/_base/declare", "dijit/form/DateTextBox", "dojo/parser"], function(declare, DateTextBox) {
    declare("dijit/form/FutureDateTextBox", [DateTextBox], {
        postCreate: function() {
            this.inherited(arguments);
            this.set('constraints', {
                min: new Date()
            });
        }
    }); 
});

As you can see we’re overriding the postCreate() function, but the first thing we do is call this.inherited(arguments);.
If we don’t do that, then the widget will not work because then we would miss the initialization of the widget (which is happening in the original postCreate() function but not in ours).
future-date-textbox

Summarized

If you only need to extend a single, existing JavaScript prototype or object, then the lang-module will be your best choice.
For Dojo modules and widgets the declare-module will be more useful.

That’s also the end of this tutorial about inheritance in Dojo.

Tagged , .

g00glen00b

IT Consultant with a passion for JavaScript. Experienced in the Spring Framework and various JavaScript frameworks.

  • romainlapierre

    Nice tutorial! I want to know more on overriding events. I would like to inherit from a FilteringSelect and then override it’s onKeyUp() event but it doesn’t work.

    What I want is a custom widget that inherit FilteringSelect and set a custom onKeyUp event inside it so I would like to call CustomWidget(“myCustomWidget”).startup() and then use onKeyUp(). Is it possible ?

    • g00glen00b

      I’m not quite sure about what you exactly mean. You can manipulate the event by overriding the private method behind it, in this case

      _onKeyUp()

      , for example:

      declare("custom/FilteringSelect", [ FilteringSelect ], {
        _onKeyUp: function() {
          this.inherited(arguments);
          console.log("test");
        }
      });
      
      • romainlapierre

        Hi, thank you this is exactly what I needed for. The only thing is that I dont know why its a private method because we use onKeyUp() when using a simple FilteringSelect but anyway you found the solution 🙂

        • g00glen00b

          Well, I’m not entirely sure either, but

          onKeyUp()

          is simply an aspect executed after

          _onKeyUp()

          Probably due to overriding the

          onKeyUp()

          function, the aspect isn’t connected anymore (since it’s a new function), and that’s why it wouldn’t work (I think).
          The

          _onKeyUp()

          function on the other hand is called directly and not through aspects.

  • Franklin dsuza

    Addition to my previous comment:
    For e.g the following translation makes little sense:

    As you can see we are going to postCreate() function to adjust but we go before we call our custom codethis.inherited(arguments); perform. If we do not, then the widget will not work because they themselves had many exciting in the postCreate() does what should not happen. override it by

  • Franklin dsuza

    hi, Can you please convert this post to english. ( Possibly all dojo post which are in dutch). I have just started learning dojo and have found your post most useful over the web.
    Would appreciate the effort that you have taken to write the post. I have tried Google translate, but find it a bit difficult to understand the translated post as the translation omits important keywords (verb) in the sentences.
    Would like to suggest that you can use the google translate yourself and edit the translated page to correct it. This should save you time rather than rewrite the whole post from scratch.
    Would be happy to see more post from you on dojo.
    thanks.

    • g00glen00b

      I’ll translate them as soon as I can. 🙂 But this may take some time, any preference of which tutorial should be translated first?

      • Franklin dsuza

        hi Dmitri,

        Can you please translate in the below order.

        1.dojo-inheritance-overriding-extending

        2.Dojo-publisher-and-subscriber

        3.Dojo-domReady-vs-ready

        4.dojo-promises-deferreds

        5.Dojo-require-vs-define

        I have translated using google translate all the Dojo tutorial pages(Dojo_Tutorial.rar) of this website and mailed to you company id. Hope this reduces the effort for you. This is the least i can do.

        Thanks!

        • g00glen00b

          Hey! I just translated this tutorial. The other ones will be done as well, but probably in the next days/weeks.

          • Franklin dsuza

            thanks for the effort taken!
            Cheers!!!

        • g00glen00b

          Both the tutorial about the publisher/subscriber and domReady/ready have been translated as well.