DojoML - The best thing since sliced bread!

Have you ever found a widget that does almost *everything* you need, yet you still manage to find some small thing that needs to be done differently? Ever cringe at the thought of having to extend a widget to add 5 or 10 lines of code to it? In the past, thats what you had to do… well unless you were really gung-ho and wanted to write your own from scratch.

We weren’t too fond of that idea here, so something called DojoML was born. Essentially it means Dojo Markup Language and its purpose is to allow hot patching of JS code in very specific ways.

DojoML currently provides two main functions, dojo/connect and dojo/method, which both do exactly what they sound like. Dojo/connect allows markup driven dojo.connect’s to be made and dojo/method are markup driven method definitions. Lets start with a quick example of dojo/method.

Lets say you have a Modal Dialog:

<div id="myDialog" title="Initial Content">my dialog content
</div>

.. and you wanted an easy way of updating its title… Now you could do it the hard way… and everytime you want to update the title do dijit.byId(’myDialog’).titleNode.innerHTML = “my new title”; but that gets old and looks horrible as far as code readability goes… now if only there was an easy way to fix this… Well there is…

<div id="myDialog" title="Initial Title">
		<script type="dojo/method" event="setTitle" args="title">
	        //this is ALWAYS the widget the dojoML is contained in
	        this.titleNode.innerHTML = title;
	    </script>
    my dialog content</div>

This means that you can do dijit.byId(’myDialog’).setTitle(’my new title’); and never have to deal with titleNode.innerHTML again! A few things to note about this method…
1) Its functionally equivalent of doing dijit.byId(’myDialog’).setTitle = function(title) { this.titleNode.innerHTML = title; }
2) Due to #1 this.inherited(arguments) will not work
3) Due to #1 this also means any previous declaration of “setTitle” will be lost…

Now lets look at dojo/connect we’ve already said that it works as a markup driven dojo.connect so lets find a use for it… say you wanted a certain dijit.Dialog to *always* have a particular layout to its content… For example; a status icon that appears at the top left everytime you call setContent on this Dialog… we could use dojo/connect to create a wrapper to setContent that would do this… Dojo/connect also accepts the same parameters that dojo/method does, event and args. If you specify no event then that block is executed once the widget is instantiated after postCreate is called, this is what we’re going to use to patch our dialog.

<div id="dlg">
    <script type="dojo/connect">
		//fire once the dialog is created
	    //this is ALWAYS the widget the dojoML is contained in
	    this.realSetContent = this.setContent;
	    this.setContent = function(content){
	        this.realSetContent('<img style="float:left;" src="/images/dialogIcon.gif" />' + content);
	    }
	</script>
    Initial dialog content here!</div>

Notes:
1) functionally equivalent to dojo.connect(myDialog, ‘postCreate’, function() { ….. });

Now once this dialog is instantiated… you will have a new function, “realSetContent” and a newly rewritten “setContent” function that now prepends an icon to all content that you send it! And if you wanted something to run everytime your dialogs setContent was called… you could use a dojoML block like so

	<script type="dojo/connect" event="setContent" args="content">
	    //note event: method we connect to
	    //note args: comma delimited list of arguments to handle
	    console.debug(this, " just called setContent(", content, ")");
	</script>

Notes:
1) Functionally equivalent to dojo.connect(myDIalog, ’setContent’, function(content){ console.debug(this, ” just called setContent(”, content, “)”);});

This shows a basic example of how to use both DojoML methods… keep in mind this allows you to “rewrite” widgets without extending them but if you end up with a bunch of this type of script blocks for 1 widget… you are probably better off creating an extended widget in your own namespace so that a prototype object can be created from it and used to create future ones at a much lower CPU cost.

It is worth noting: The above setTitle implementation is a workaround. As of Dojo 1.2, each widget will support an .attr() method, which will set the attributes for you. In the case of setTitle in a Dialog, you would simply: dijit.byId(”myDialog”).attr(”title”, “New Title”);

Tags: