Part 4: "More on Widgets"

P

Advanced ContentPane Usage

Introduction

A common use case for DHTML/ajax is to fetch a fragment of html using XHR or some other way, and change the innerHTML of a div with that content. Problem with this is that it doesn't instanciate widgets and doesn't fire scripts. ContentPane was created to make widgets and scripts work and reduce the potential for memory leaks. ContentPane is a base widget for many (Html)widgets, it handles remote loading as well as local setting of content and instanciating widgets in that content. Think of it as islands in your page that can easily switch content using setContent() or setUrl().

Many other widgets inherits ContentPane, like Tooltip, Dialog, FloatingPane etc. That means that all the methods and properties of ContentPane also applies to them.

ContentPane is often used as children of Layout widgets like LayoutContainer, TabContainer, AccordionContainer

Dont misstake it for a Iframe though, It should not be used on very large html fragments.

Usage

Simple usage ... <div id="cpane" dojoType="contentPane" href="initialContent.html"><div> <a href="javascript:dojo.widget.byId('nextContent.html')">Goto nextPage</a> ...

Basic options

  • loadingMessage Default: "Loading..." Set a custom loading message, see onDownloadStart to avoid showing this message completely
  • adjustPaths Default: true When content is setUrl'ed from a different folder paths to images, links etc. is adjusted so the point to the correct dir
  • href Default: "" Use this to grab initial content when contentpane is created.
  • extractContent Default: true Only insert the html that is inside Script and style tags are not affected by this setting
  • parseContent Default: true Create widgets inside content
  • cacheContent Default: true Use dojo.io.bind javascript cache and, if it exists, browsers cache
  • preload Default: false Lazyload switch, if true it will download content even if domNode is hidden Note: To make use of the default lazyload setting you need to hide your domNode Like this: <div dojoType="Dialog" style="display:none;"></div>
  • bindArgs Default: {} Send in a custom setting to the dojo.io.bind call, like: mypane.bindArgs = {sync: true, preventCache: false}; mypane.seetUrl('nextHtmlFragment.html');
  • refreshOnShow Default: false Re-download content each time ContentPane is shown again
  • executeScripts Default: false Fire scripts in content Note: see scriptSeparation
  • scriptSeparation Default: true Run scripts in a separete scope for ContentPane Note: set to false if you want similar behaviour as a normal pageload
  • handler Default: "" Java function name, generate pane content
  • isLoaded Default: false Tells wheather we are loaded or not, see also: onLoad() and addOnLoad()

Methods apart form those provided by ContentPane's superclass HtmlWidget

  • setContent(String or DomNode) Use this instead of innerHTML
  • setUrl(String or dojo.uri.Uri) Use this to set a new href and download and diplay that href
  • refresh() Re-download and display href
  • loadContents() Like refresh but only when isLoaded is false
  • setHandler(Function) Set a function callback for javacontent generation
  • abort() Abort a async download
  • addOnLoad(Object, "functionname" or Function) Push a callback that will be run when content the next onLoad occurs. It's a fire and forget stack, if you want a callback each onLoad, see onLoad() Works for setContent as well
  • addOnUnload(Object, "functionname" or Function) Same as addOnLoad but for onUnLoad event

Methods (Intended as event hooks using dojo.event.connect)

  • onLoad() Called when everything rendered initialized and ready
  • onUnload() Called before content is cleared
  • onDownloadStart(e) preventDefault'able Called before a download occurs To prevent showing the loading message, do like this:
  • onDownloadEnd(url, string) Called when download is completed, before it is setContent'ed
  • onDownloadError(e) preventDefault'able Called when a load error occures, before The load error message is displayed. Prevent it the same way as onDownloadEnd Tip: During debug, you can display debug info like responseHeaders, responseText etc.
  • onContentError(e) preventDefault'able Called when content insertion generates a error, before error mesage is displayed, like DOM faults, dojo.require() *syntax* faults etc.
  • onExecError(e) Called when there is errors evaling script, doing java setContent and download errors of external scripts
  • In order to prevent the default messages you can do something like this:

    <script>
    	var myLoadMessage = {
    		show: function(event){
    			event.preventDefault();
    			... custom code here
    		}, 
    		hide: function(){...}
    	}
    
    	dojo.addOnLoad(function(){
    		var pane = dojo.widget.byId('myPaneId');
    			dojo.event.connect(pane, "onDownloadStart", myLoadmessage, "show");
    	});
    </script>
    <div dojoType="ContentPane" id="myPaneId">...startcontent...</div>
    
    or
    
    <div dojoType="ContentPane" 
    	onDownloadStart="myLoadMessage.show(arguments[0]);">...startcontent...</div>
    		

When used as a child to TabContainer, AccordionContainer or PageContainer TabContainer, AccordionContainer or PageContainer extends Widgets with these extra options

  • label Tab text
  • selected Preselect this tab after creation
  • closable Display close button
  • Note:
  • Height and width settings is done on the Container, not the ContentPane.
  • In order for lazyload to work you have to hide your domNode initialy

When used as a child of LayoutContainer LayoutContainer package extends Widgets with this option

  • layoutAlign "left", "right", "bottom", "top", or "client" see layout section of the book for more info

FAQ

  • Why doesn't my widgets show up? Most likely you have used mypane.domNode.innerHTML = htmlstr; Use setContent instead: mypane.setContent(htmlstr);
  • Why doesn't lazy load work? You have to hide your domNode, style="display:none;", initialy while creating ContentPane
  • ContentPane displays strange looking characters when loaded remotly in some browsers, why? Like all server communication your browser need to know what charset your html is encoded with. Make sure your server is sending the correct Content-type header. example in php: header("Content-type: text/html; charset=utf-8"); Make sure you type utf-8 and not utf8, else IE will generate a warning and bail out.
  • Why is ContentPane so slow? You probaly send it a big chunk of html with deeply nested tags.
    • Send it a html fragment, not a complete page with doctype and everything
    • Try to make the HTML simpler and use css for styling
    • Turn off the options you dont need: adjustPaths, extractContent, executeScripts, parseContent
    • Consider redesigning your page with serveral ContentPanes which grabs a smaller portion of your html that way you dont have to scan, render and create as many DomNodes/Widgets on each update.
    • Perhaps dojo.io.updateNode("nodeId", "myUrl") is all you need, and ContentPane is to heavy for your needs
  • dojo.addOnLoad() is called to early, before my Content is loaded See onLoad event and addOnLoad for ContentPane, it is usualy easier to do this using <script>_container_.addOnLoad(..)</script> in your downloaded page, just be sure to set executeScripts=true
  • My inline scripts doesnt work when loaded in ContentPane, I have turned executeScripts to true? Short answer: set scriptSeparation=false Long answer: ContentPane separates scope of scripts between different ContentPane's see: scriptScope page in dojo book
  • When I press submit in my form inside a ContentPane, the whole page unloads, why? ContentPane doesn't have a form handling feature, look at dojo.widget.Form or see sample use case below
ContentPane examples

    scriptScope

    Executing scripts in ContentPane in dojo-0.3.1





    This is a explanation about scripthandling in a ContentPane in other words when you set executeScripts to true, it is false by default.


    ContentPane has some convenience functions related to scripts that makes life easier:



    • .addOnLoad()
    • .addOnUnLoad()
    • And the replacement for scriptScope to dojo.widget.byId('thisWidgetId').scriptScope in html content attributes.


    All scripts within content is evaled in the ContentPane property scriptScope, this means that you can have 2 or more content panes in the same page with the same name without risk of collision. This implementation does have its pros and cons. but correctly handled they will be useful. Also the scripts evals before widgets is parsed and after html is inserted.



    So the content scripts is evaled inside a freestanding scope that inherits window. Take this function declaration.



    <script>
        var i = 0;
        function addToI( j ){
            i = i + j;
            return i;
        }
    </script>
    
    becomes (from window scope):
    
    (function( ){
        var i = 0;
        function addtoI( j ){
            i = i + j;
            return i;
        }
    })
    


    Due to the way javascript works addToI will be private and you cant reach it. Read Douglas Crockford excellent description of this. http://javascript.crockford.com/private.html



    You can fix this in a couple of ways, one is to append it to global



    <script>
        i = 0; // note lack of var
        addToI = function( j ){ // we could also do window.addToI = function( j ){ ...
            i = i + j;
            return i;
        }
    </script>
    
    becomes (from window scope)
    
    i = 0;
    function addtoI( j ){
        i = i + j;
        return i;
    }
    


    This is'nt recommended, if you want global scripts It is better to include them in the main page the old fashion way.



    A better way would be to use Privileged functions:



    <script>
        this.i = 0;
        this.addToI = function( j ){
            this.i = this.i + j;
            return this.i;
        }
    </script>
    
    becomes (from window scope)
    
    (function(){
         this.i = 0; // now it is a property of this function and can be reached from the outside.
         this.addToI = function( j ){
            this.i = this.i + j;
            return this.i;
         }
    })
    


    As you might have guessed by now the (function(){...}) is the function scope that is reference held by ContentPane.scriptScope.

    So to call the addToI function from the outside we can do:

    var added = dojo.widget.byId('myPaneId').scriptScope.addToI( 10 );
    dojo.debug(added) // prints 10
    
    added = dojo.widget.byId('myPaneId').scriptScope.addToI( 10 );
    dojo.debug(added); // prints 20
    
    and so on...
    
    Now lets say we have html that would like to alert the value of i (this.i) in plain html that would be:
    <button onclick="alert(i);">Tell me i !</button> 
    // As explained above this wont work in ContentPane(unless you set it to global by omitting var).
    
    Now if we now the ID of the contentPane that pulls in this content we could do:
    <button onclick="alert(dojo.widget.byId('myPaneId').scriptScope.i);">Tell me i !</button>
    // this will work in ContentPane
    
    That is'nt very useful as we don't always know the ID of the contentPane that pulls in the html when we write the content.



    ContentPane has a convenience replacer function the scans the html and replaces all occurrences of the keyword scriptScope in html attributes. So to achieve the above:
    <button onclick=" scriptScope.i">Tell me i !</button>
    // this will work in ContentPane
    // NOTE: Due to a bug in ContentPane 0.3.1 you need to add a extra space before the keyword
    // Thank you Sasha Firsov for finding that! 
    
    The parent scope of scriptScope is window, the reason for that is to avoid messing with widget internals. Just imagine the disaster a redefinition of setUrl function would cause otherwise.



    To enable the content scripts to talk to the containing ContentPane there is set a private variable on scriptScope construction.

    That is the _container_ variable. Lets say we have a widget in our content that we would like to connect a event callback on:
    ... some content
    <div dojoType="DatePicker" id="myPicker"></div>
    ... rest of content
    
    A content script could look like:
    <script>
        var o = {
            storeDate: function( ){
                var datePick = dojo.widget.byId('myPicker');
                var date = datePick.storedDate;
                // save date somewhere
            }
        };
        _container_.addOnLoad(function(){
            var picker = dojo.widget.byId('myPicker');
            dojo.event.connect(picker, "onSetDate", o, "storeDate");
        });
    
        // remember to disconnect onUnLoad, very important!!
        _container_.addOnUnLoad(function(){
            var picker = dojo.widget.byId('myPicker');
            dojo.event.disconnect(picker, "onSetDate", o, "storeDate");
        });
    </script>
    


    When the content is cleared in ContentPane the scriptScope is unreferenced, this means that if there are no other variables that holds reference to any of the scriptScope objects (like event connects, varible connection etc), the script will be garbage collected by the javascript engine (memory freed).



    Different browsers are more or less conservative about GC (garbage collect), IE and Mozilla beeing the more relaxed browsers, khtml and Presto (Opera) engines are more conservative.



    Some usecase samples

    Lets say you have page that you need to run in as both stand alone and within a ContentPane.

    Then you can do something similar to this.

    Courtesy of Sasha Firsov for a pointer to this example!
    <html>
    <head>
    <script>
        var djConfig = {isDebug: true};
    </script>
    <script src="dojo/dojo.js"></script>
    <script>
        var scriptScope = this;
        if(typeof _container_ == 'undefined'){
            var _container_ = dojo;
        }
    
        _container_.addOnLoad(function(){
            dojo.debug("Successfully loaded!");
        });
    
        this.doWhenClicked = function(txt){
            dojo.debug(txt);
        }
    </script>
    <body>
        <a href="javascript:scriptScope.doWhenClicked('You clicked a link!');">Click here!</a>;
    </body>
    </html>
    




    Perhaps you want to prevent all <a href='...' link clicks with a ContentPane from clearing your page, and use the href to set your client ContentPane.
    *********Your mainpage*********** 
    <html>
    <head>
    <script src="dojo/dojo.js"></script>
    <script>
        dojo.require("dojo.widget.ContentPane");
        dojo.require("dojo.widget.LayoutContainer");
    
        function changeUrlInClient(url){
            var client = dojo.widget.byId("client");
            client.setUrl(url);
        }
    </script>
    </head>
    <body>
        <div dojoType="LayoutContainer" layoutChildPriority='none' style="border: 1px solid blue; width: 800px; height: 300px;">
            <div dojoType="ContentPane" layoutAlign="left" style="width: 200px;" executeScripts="true" href="linkpage.html"></div>
            <div widgetId="client" dojoType="ContentPane" layoutAlign="client" style="border:1px solid red;"></div>
        </div>
    </body>
    </html>
    
    *******linkpage.html************
    <html>
    <head>
    <script>
        var o = {
            listen: function(evt){
                // if the onclick came from a 
    </head>
    <body>
        <a href="content1.html">content1</a>
    <a href="content2.html">content2</a>
    <a href="content3.html">content3</a>
    <a href="content4.html">content4</a> </body> </html>




    A simple example of using a form in ContentPane. NOTE! dont use this login example in real world applications, password is sent in cleartext
    **********mainpage************
    <html>
    <head>
    <script src="dojo/dojo.js"></script>
    <script>
        dojo.require("dojo.widget.FloatingPane");
        dojo.require("dojo.widget.Button");
    </script>
    </head>
    <body>
        <div dojoType="FloatingPane"
            title="Login example"
            style="width: 300px; height: 300px;"
            executeScripts="true"
            cacheContent="false"
            href="login.php">
        </div>
    </body>
    </html>
    
    *********login.php**********
    <?php
        session_start();
    
        // are we trying to login?
        if(isset($_GET["login"])){
            // this could of be a database instead
            $users = array(
                    "JohnDoe"=>
                        array("pass"=>"foo", "id"=>1),
                    "JaneDoe"=>
                        array("pass"=>"bar", "id"=>2),
                    "JuniorDoe"=>
                        array("pass"=>"baz", "id"=>3)
                    );
    
            if(isset($_POST["user"]) && isset($_POST["pass"])){
                $pass = $_POST["pass"];
                $user = $_POST["user"];
                if(isset($users[$user]) && ($users[$user]["pass"] == $pass)){
                    $_SESSION["id"] = $users[$user]["id"];
                    exit("(true);");
                }
            }
    
            //if we get here we have failed to login
            exit("(false);");
        }
    
        // logout?
        if(isset($_GET["logout"])){
            unset($_SESSION["id"]);
        }
    
        if(isset($_SESSION["id"])){
            // it is safe to show secret content
    ?>
    
        <script type="text/javascript">
            this.logout = function(){
                _container_.setUrl("login.php?logout=true");
            }
        </script>
        <h3>You have successfully logged in!</h3>
        showing secret content here
    <a href="#" onclick="scriptScope.logout();">log out</a> <?php }else{ //no it wasnt safe, show our login script ?> <script type="text/javascript"> this.ok = function(){ _container_.domNode.style.cursor = "wait"; dojo.io.bind({ formNode: dojo.byId("login"), mimetype: "text/javascript", handler: function(type, data){dojo.debug(data); _container_.domNode.style.cursor = ""; if(type=="load"){ if(data){ _container_.setUrl("login.php"); }else{ dojo.byId("message").innerHTML = "Wrong username or password"; } }else{ dojo.byId("message").innerHTML = "An error occured while login, please try again."; } } }); } this.quit = function(){ _container_.hide(); } </script> <form name="login" id="login" method="post" action="login.php?login=true"> <div id="message" style="text-align:center; color: red;">You need to login</div> <label for="user">Username: <input type="text" name="user"/>
    <label for="pass">Password:</label> <input type="password" name="pass"/> <button dojoType="Button" onClick="scriptScope.ok();"/>login</button> <button dojoType="Button" onClick="scriptScope.quit();">quit</button> </form> <?php } ?>




    In the follwing scenario you wont need executeScripts.

    In fact it wont affect the script at all, the script will run just as any other regular script in ordinary page
    <html>
    <head>
        <script src="dojo/dojo.js"></script>
        <script>
            dojo.require("dojo.widget.ContentPane");
        </script>
    </head>
    <body>
        <div dojoType="ContentPane" >
            <script>
                // this script will fire before dojo makes our parent a ContentPane widget, so it wont be 
                // affected by executeScripts at all.
                // i wont have a _container_ variable and scriptScope wont hide any variables
                // it will work just as a inlne javascript block always has
    
                alert("This alert will fire event if you have executeScripts=false");
            </script>
        </div>
    </body>
    </html>
    






    It should be fairly easy to pull in some small customized scripts that is tweaked to the content, like a form validation script or a button callback.

    The executeScripts and scriptScope has the potential to be very usefull, I cant think of all the possible implementations but probably you can.



    Catches:



    • If you event.connect to _container_ be sure to disconnect onUnLoad, else you get all sorts of strange errors
    • Be sure to unref. all references into and out of scriptScope before setting new content, else there will be a memleak
    • <div dojoType="dijit.form.Button"> <img src="images/flatScreen.gif" width="32" height="32"> <span style="font-size:xx-large">big</span> </div>

      Like HTML buttons, the dojo button sizes to fit its content. Usually, you will provide an onclick="..." attribute to specify what happens when the button is pressed.


      This needs to be rewritten for 0.9

      Using Your Own Backgrounds

      By default, dojo uses a blue gradient background. But you can provide your own. You will need to create three .gif images: one for the left, one for the right, and one for the center. The filenames must end with l, r, or c, respectively. You can specify image sets for four different conditions:

      • activeImg - the mouse pointer is over the button
      • inactiveImg - the mouse pointer is not over the button
      • pressedImg - the button is being pressed
      • disabledImg - the button cannot be pressed

      For example, you can use these files:

      • /images/buttons/disabled-l.gif
      • /images/buttons/disabled-r.gif
      • /images/buttons/disabled-c.gif

      as the disabled image of your button like this:

      <button dojoType="Button" disabledImg="/images/buttons/disabled" >
         Quit
      </button>
      

      API Reference: dojo.widget.Button

      See Also: DropDownButton, comboButton

    comboButton

    Used in HTML Element:button

    A combination Button and DropDownButton. Use this for a button that has a common action (e.g. "Make Regular Dinner") and less common related actions (e.g. "Make Romantic Dinner" and "Make TV Dinner")

    Example

    <button dojoType="comboButton" menuId='saveMenu'>
       <img src="images/editIcon.gif" width="32" height="32">
       Save
    </button>
    
    <div dojoType="PopupMenu2" id="editMenu" toggle="wipe">
    	<div dojoType="MenuItem2" iconSrc="images/save.gif" caption="Save" accelKey="Ctrl+S" onclick="mySave();" />
    	<div dojoType="MenuItem2" iconSrc="images/saveAs.gif" caption="Save As...." accelKey="Ctrl+A" onclick="mySaveAs();" />
    </div>
    
    

    You can also specify your own background images, as in Button.

    API Reference: dojo.widget.ComboButton

    See Also: Button, DropDownButton, PopupMenu2, MenuItem2

    Editor2 (RichText) Widget

    Introduction

    Editor2 Widget in dojo provides a WYSIWYG editor for HTML content. The core is compact and lightweight, while a plugin framework ensures that any functionality can be achieved by plugins.

    Basic html editing capacity is implemented in the core, which is the RichText widget. Currently keyboard shortcuts are also hardcoded in this widget (TODO: generalize this, or use KeyRouter instead?).

    Editor2 is a subclass of RichText Widget which adds a toolbar (Editor2Toolbar Widget) to the top of the editing area.

    Basic Principles of Editor2

    In order to have an extensible structure, the new Editor2 introduced several new concepts.

    Command (dojo.widget.Editor2Command)

    The first and most fundamental one is call Command, which executes a specific function on the editing area. It also provides the API to retrieve the current state of the command. The base class for Command is dojo.widget.Editor2Command (defined in Editor2.js).

    Each command should have a unique name and each command is a singleton object per page: no matter how many Editor2 instances there are in one page, they share the same command objects.

    Toolbar Item (dojo.widget.Editor2ToolbarButton)

    The toolbar (defined in Editor2Toolbar.js) for the editor2 contains serveral toolbar items. The basic class for toolbar item is dojo.widget.Editor2ToolbarButton (defined in Editor2Toolbar.js), which essentially is a simplified version of dojo widget.

    Toolbar item can be of any type, besides buttons, you can have more complex items, such as a dojo combobox like item with a dropdown (see dojo.widget.Editor2ToolbarFormatBlockSelect in Editor2Toolbar.js).

    Available Plugins

    All the builtin plugins are located under src/widget/Editor2Plugin directory. Those files ending with Dialog are the actual popups. The table below lists all the other plugins:



    NameDescriptionFeatures
    ContextMenuCommandToolbarItem
    ContextMenuContext Menu Core, with menu items for builtin commandsCut/Copy/Paste, Link/Unlink, Image properties--
    FindReplaceImplement find and replace functionalities-Find/ReplaceFind/Replace
    TableOperationSupport for table related operationInsert/Delete TableInsert/Delete TableInsert Table
    AlwaysShowToolbarEnsure the toolbar is visible when scrolling the page---
    ToolbarDndSupportToolbar Set/Item drag and drop support---
    SimpleSignalCommandsAdd simple signals to Editor2, such as save() and createLink()---

    Misc

    SetupCopyPasteForFirefox - Copy, Cut and Paste are disabled by default in Mozilla/Firefox, this tip is how to enable it for your trusted websites.

    Form Widgets

    There are many widgets used for forms:

    • Button - just like HTML's button, except with a few advanced features
    • Checkbox - like HTML's checkbox but in soria (blue) theme
    • ComboBox - like a text input field, but w/suggested values
    • DropDownDatePicker - for specifying a date by selecting a cell of a calendar
    • DropDownTimePicker - for specifying a time (scheduled for 0.4 release)
    • Editor2 (RichText) - like HTML's textarea, but allows editing of rich text

    • HslColorPicker - pick a color
    • Select - just like HTML [select] element, except w/autocompletion, and loading of possible values from a remote data source
    • Slider - graphical slider control used to specify a number within a range
    • Spinner - numeric input field that can be adjusted up/down by pressing arrow keys
    • dojo.widget.*Validate - a bunch of widgets that check the user's input and correct it or print an error message if it doesn't conform to a certain format

    The main principle of these widgets is that:

    • each widget corresponds to a native HTML element.
    • each widget (w/the exception of Button) represents a single input value
    • each widget has a (possibly hidden) <input> element, to which it serializes its input value, so that form submission (either normal submission or via FormBind) works as expected

    All these widgets should have these attributes just like native HTML input elements. You can set them during widget construction, but after that they are read only:

    • disabled
    • tabIndex
    • name
    • value

    And they also share some common methods:

    • disable()/enable()
    • onValueChanged() - called with the new value of the widget whenever it's changed
    • setValue() (note: you can get the value by accessing accessing value; exception: Editor)

    (note: some widgets don't conform but we plan to convert them soon)

    Author: Bill

    ComboBox Widget

    The ComboBox widget is a text input box that supplies a list of possible preexisting values for the user to choose from. It can query the server for an updated list of values as the user types, allowing it to offer a large list of values without requiring the entire list to be downloaded to the browser.



    To demonstrate the power of this widget, let's dive right in and make an autocompleting combo box that queries the server for the list of matches for what the user has typed so far. We'll declare the widget using HTML:

    <select dojoType="ComboBox"
        autoComplete="true"
        dataUrl="/suggest.php?match=%{searchString}"
        maxListLength="15"
        mode="remote"
        name="myComboBox">
    
    There are a few attributes of note here.

    • autoComplete should be set to "true" if you want the widget to fill in the rest of the input box with the contents of the first item in the suggestion list. For example, if the user has entered "Ala" and the first suggestion on the list is "Alabama", the widget will put "bama" after the letters the user has typed (selected, so the user can simply continue typing to replace them with something else.)
    • dataUrl is the location of a URL which will be queried each time the user types something into the box. It will be discussed in more detail below.
    • maxListLength is the number of suggestions that will be visible to the user at one time. If the server supplies more suggestions than this, the user will have to scroll the list of suggestions to see them.
    • mode is one of "local" (the default; the dataUrl is fetched once at load time), "remote" (the dataUrl is fetched on each keypress, and is expected to return a JSON object; see below) or "html" (the dataUrl is fetched on each keypress, and is expected to return HTML.)
    When the page containing that widget is loaded, it gets rendered as a text entry box with a little dropdown list button on the right side, not as a normal HTML <select>.

    When you enter text into the box, Dojo tries to find matches for the text you've just entered. For example, suppose you type "a". Because the widget's mode is set to "remote", it will fetch the dataUrl and substitute your input for the magic %{searchString} token. (That token is only valid when mode is set to "remote" or "html".) In this case, it will fetch /suggest.php?match=a from the server. You don't have to use PHP on the server side, of course; it's simply used as an example here. The point is that the widget will replace the magic token in dataUrl with the user's input and fetch the resulting URL from the server.

    What should the server return? In the "remote" mode, the widget expects a JSON array of entries, each entry of which contains a displayable option name and a value. For example, the server might return something like

    [

    [ "Alabama", "AL" ],

    [ "Alaska", "AK" ],

    [ "Arkansas", "AR" ]

    ]

    Many server-side programming languages have existing libraries to output native objects in JSON form. In this case, for simplicity's sake, we'll do it by hand. Here's what an extremely simple, inefficient suggest.php might look like.

    <?php

    $states = array( "Alabama" => "AL",

    "Alaska" => "AK",

    ...

    );



    $userInput = $_GET['match'];

    $result = "[";



    foreach ($states as $state => $abbreviation) {

    if (strpos($state, $userInput) === 0) {

    $result = $result . '[ "' . $state . '", "' .

    $abbreviation . '"],';

    }

    }



    $result = $result . ']';

    print $result;

    ?>



    FormBind

    FormBind allows you to quickly setup your “Web 1.0″ form for asynchronous submission. Basically it sets things up so that whenever the user hits the submit button, rather than submitting the form in the usual way, and refreshing the entire page, the contents are sent over xmlhttp (or any transport), and then the results are passed to the given callback.

    How do you do it? Easy:

    function magicForm() {

    var x = new dojo.io.FormBind({

    // reference your form

    formNode: document.forms[1],



    load: function(load, data, e) {

    // what to do when the form finishes

    // for example, populate a DIV:

    dojo.byId('myDiv').innerHTML = data;

    }

    });

    }



    dojo.addOnLoad(magicForm);

    Note the unfortunate naming between dojo.io.bind() and dojo.io.FormBind.

    dojo.io.bind() is a function that immediately sends the given info to the specified URL. (It would probably better be called something like dojo.io.send() but it isn't.)

    dojo.io.FormBind(), on the other hand, doesn't send anything to the server. It just hooks up events so that when the user presses the submit button then the data is sent via dojo.io.bind(). Also note that you call "new" to make it work.

    Note also that although dojo.io.bind() also takes a formNode argument, it's tricky to use and you are better off using FormBind. That's because for forms containing the Editor/Editor2 widgets, they need to serialize their data back to the [textarea] before the form is submitted, and that only happens when the form's onSumbit handler is called. Just calling dojo.io.bind() and specifying a formNode won't do that. However, with FormBind (and with an actual [input type="submit] button in in the form), everything works perfectly.

    You can play with the demo to see it in action.

    Validation

    There are three methods which you can use to validate your form data on the client side before it is sent to the server - with each having their own benefits and drawbacks. Often, the most effective validation is performed using a combination of these methods.

    It is important to understand that whilst client side validation is effective, it should not be considered a replacement of server side validation techniques. In order to provide an enjoyable and secure user experience it is essential that a combination of both methods is used.

    Manual processing of input fields

    The dojo.validate.* module provides a number of functions for validating user input. Currently, these functions are broken into the following groups:
    • common
    • datetime
    • de
    • jp
    • us
    • web

    Common dojo.validate functions

    dojo.validate.isText

    This function takes two arguments - a value, and an optional flags object. Depending on the properties of the flags object (length, minlength, or maxlength), the value can be tested for exact length, a minimum length, or a maximum length.

    dojo.validate.isInteger


    This function takes two arguments - a value, and an optional flags object. Depending on the properties of the flags object (signed or separator), the value can be tested for the presence of a sign (+ or -) character at it's beginning, or whether it has separators. Whilst there is no default separator, single characters (such as ',') or arrays listing multiple separators can be specified.



    dojo.validate.isRealNumber


    dojo.validate.isCurrency



    dojo.validate.isInRange


    dojo.validate.isNumberFormat


    TODO: summary of each function group and functions within each group

    Using dojo.validate.check on a form

    The second method of validation is known as dojo.validate.check. This function let's you setup a table of rules for checking a form's input elements.

    TODO: more info on this

    Validation widgets

    There are several widgets in the dojo.widget.validate module that will either correct user input (converting lowercase to uppercase, etc.), or print errors when the input doesn't match a certain pattern. A few of the widgets are:

    • dojo.widget.validate.IntegerTextbox - allow only integer input
    • dojo.widget.validate.UsZipTextbox - entering a US zip code
    • dojo.widget.validate.UsPhoneNumberTextbox - entering a US phone number
    Unfortunately currently the validation widgets, although they do display an error message alongside illegal values, do not actually prevent the form from submitting. This needs to be addressed at some point. You need to do some javascript coding yourself to make this happen.

    Graphical widgets

    The alternative to validating user input is to provide such an interface that the user can't enter a bogus value to begin with. For example, the DatePicker widget won't let the user input an invalid date.

    Interacting With Widgets

    Calling Methods

    Previously I talked about the widget object, that you can get access to like this:

    var myButton = dojo.widget.byId("foo");

    If you create a widget programatically you automatically get a pointer to the widget object:

    var myButton = dojo.widget.CreateWidget("Button", {caption: "click me"});

    What is myButton useful for? Calling methods on the button. For example:

    myButton.setCaption("Don't press me!!");

    Note that doing the following won't work, because the myButton object doesn't know that the caption variable has been changed:

    myButton.caption="this won't do anything";

    Also note that to disable/enable a widget, call disable()/enable(), rather than setting the disabled attribute directly. People often make that mistake.

    Read only Variables

    There are some read-only variables, however, that are useful to access. Two of the most important ones are:

    • domNode - points to the node that replaced your original markup (the [button] tag in the example above)
    • containerNode - points to the node that contains the contents of the original markup ("Click me" in the example above)

    That reminds me. In the above example of programmatic creation, you also need a line like this:

    form1.appendChild(myButton.domNode);

    Events

    Consider the markup below:

    <button dojoType="Button" onClick="alert('hello world')">

    It looks familiar, but it's actually quite different than the normal onclick handler on the dom node.

    onClick() is a method in the Button widget object. It's got a similar name to DOM node's onclick (but not identical; there's a capitalization difference). However, it's not the same. As another example, consider

    <input type="Slider"
    
    	onValueChanged="alert('new value is ' + arguments[0]);">...

    In this case, we are using a function of the widget called onValueChanged(newValue), that has no direct equivalent in the dom world.

    Attaching vs. Overriding

    in the case above, the specified code will be run in addition to the widget's original onValueChanged() method. It works the same way as dojo.event.connect(). On the other hand, if you just specify a function name like this:

    <input type="Slider"
    
    	onValueChanged="doit">...

    Then you are overriding the widget's onValueChanged() funtion w/your own.

    Usually, the widget will provide an empty function stub, so it won't matter if you connect to it or override it.

    Using dojo.event.connect directly

    You can also do something like this, although it seems more difficult than the method above:

    dojo.event.connect(myButton, onValueChanged, function(x){
    
    	alert("new val is " + x);
    
    });

    Show and Hide

    Widgets all can be hidden (made invisible) and shown:

    • myButton.show() - display
    • myButton.hide() - make invisible
    • myButton.toggleShowing() - switches between show() and hide()
    • isShowing() - is widget currently displayed?

    For show and hide, there are 4 transitions available, that you set at widget creation time:

    • plain
    • fade
    • wipe
    • explode

    They are set like this:

    <div dojoType="FloatingPane" toggle="fade" toggleDuration="250">

    The explosion effect (often used for tooltips) also requires a point/square from which the element explodes out of, or implodes back into. This is set automatically when using the Toggler or TaskBar widgets.

    Layout

    Introduction

    There are two philosophies to laying out the screen. One way, the "web-way", says that everything should flow naturally from HTML, meaning basically that a bunch of stuff is in the document and if your window isn't big enough, then you use the browser's scrollbar. This is the way traditional web pages work, and is the best choice for many applications.

    There other philosophy is to take the available size of the viewport (basically, the browser window), and then to partition it into smaller and smaller pieces. If you think about a mail application that splits the screen into top/left/right sections, then you are thinking about this kind of design.

    The Layout Widgets

    Dojo provides a number of widgets for implementing the second design listed above. They fall into two basic categories.

    Widgets that split the screen space between a set of widgets

    • LayoutContainer- lets you position the children into top/left/bottom/right positions, with the specified center piece taking all the remaining space
    • SplitContainer- shows children either horizontally or vertically aligned, and you can adjust the relative size of each child by moving the divider bars between the widgets

    Widgets that hold mulitple children but only display one at a time:

    • TabContainer - names of children are printed as tab labels
    • AccordionContainer - stacks children vertically, and you can show one at a time
    • WizardContainer - go through the children in an ordered fashion like a wizard

    In addition, there's one widget that isn't a layout widget per se, but it is often used with the layout widgets:

    • ContentPane - like a div, but it's a widget, and it can load its contents from a specified href.

    These widgets can be nested to arbitrary levels, so that you could have a LayoutContainer with a top/bottom/client section, where the client section is a SplitContainer, and that SplitContainer could contain a TabContainer, which would itself contain a LayoutContainer, and so on.

    The leaf nodes of this hierarchy could be any non-layout node, but often are ContentPane nodes.

    Example

    Example (currently not displaying correctly. wiki needs upgrade?!):

    <DIV> <DIV>hello world </DIV> <DIV> <DIV>left side of split </DIV> <DIV> <DIV>second tab </DIV> <DIV>i'm on the bottom </DIV> </DIV>
    <DIV dojoType="LayoutContainer" >
    	<DIV dojoType="ContentPane"> hello world </DIV>
    	<DIV dojoType="SplitContainer">
    		<DIV dojoType="ContentPane"> left side of split </DIV>
    		<DIV dojoType="TabContainer">
    			<div dojoType="LayoutContainer">
    			 ..
    			</DIV>
    			<DIV dojoType="ContentPane"> second tab </DIV>
    		 </DIV>
    	  </DIV>
    	 <DIV dojoType="ContentPane"> i'm on the bottom </DIV>
    </div>
    

    Note that all these objects are called containers because they just contain a set of other objects; they don't contain mixed content (text and nodes) like a normal <DIV>.

    Also note that there is no "LayoutContainerChild" or "SplitPaneContainerChild" like node. That's to reduce the amount of markup and code required to setup a deep hierarchy of layout widgets.

    Parameters

    Note that the example above is missing some important parameters. For one thing, it doesn't specify whether the SplitContainer arranges its children vertically or horizontally. For that we need:

        <DIV dojoType="SplitContainer" orientation="horizontal">
    
    We are also missing the labels for each of the tabs in the TabContainer, which we can add like this:
        <DIV dojoType="TabContainer">
          <DIV dojoType="LayoutContainer" label="Tab 1">
           ..
          </DIV>
          <DIV dojoType="ContentPane" label="Tab 2"> second tab </DIV>
        </DIV></DIV>

    Note that the labels are specified as parameters to the ContentPane and LayoutContainer, the children of the TabContainer, rather than as arguments to the TabContainer itself. "label" is not technically a property on those two objects, but you can still specify it,and the TabContainer will pick it up.

    Similarly, for a LayoutContainer, you need to say where each child should be located:

    	<DIV dojoType="LayoutContainer">
    	<DIV dojoType="ContentPane" layoutAlign="top"> hello world </DIV>
    	<DIV dojoType="SplitContainer" layoutAlign="client">...</DIV>
    	<DIV dojoType="ContentPane" layoutAlign="bottom"> i'm on the bottom </DIV>
    	</DIV>
    

    You may freely mix sides (top, bottom, left, right) in a layout container. Sides are used from the outside in. The special side name "client" will fill in any part of the container that is not otherwise occupied. Very often you will use fixed-size side panes and a client pane that grows and shrinks as the user resizes the window, for example:

    <DIV style="OVERFLOW: hidden; WIDTH: 100%; HEIGHT: 100%" dojoType="LayoutContainer">
    
      <DIV dojoType="ContentPane" layoutAlign="top" height="2em">
    
        Page header goes here; it stretches across the whole width of the window.
    
      </DIV>
    
      <DIV dojoType="ContentPane" layoutAlign="bottom" height="1em">
    
        And a footer here, also stretching across the whole width.
    
      </DIV>
    
      <DIV style="WIDTH: 120px" dojoType="ContentPane" layoutAlign="left">
    
        Some left-side navigation HTML, bounded by the header and footer
    
        since they were already added to the layout.
    
      </DIV>
    
      <DIV style="WIDTH: 60px" dojoType="ContentPane" layoutAlign="right">
    
        Some right-side navigation HTML
    
      </DIV>
    
      <DIV dojoType="ContentPane" layoutAlign="client">
    
        Main page body here, bounded by all the fixed-size elements above.
    
      </DIV>
    
    </DIV>

    Sizing

    For the top level layout container in a hierarchy, you need to specify a size. If you don't, the contents of the container may be displayed oddly or not at all.

    <DIV style="WIDTH: 500px; HEIGHT: 500px" dojoType="LayoutContainer"></DIV>

    Many web applications will want to fill the whole screen with their top level layout container. Think of a case like a mail application. For any size browser window, you want the top part to have some menu choices, and then have the bottom part be split between a tree on the left and message list/message on the right.

    In this case, you need CSS like this:

    html, body, #mainWindow {
    
     width: 100%;
    
     height: 100%;
    
     overflow: hidden;
    
    }

    And then inside your tag you will have something like:

    <DIV id=mainWindow dojoType="LayoutContainer"></DIV>

    Programmatic creation

    Creating a hierarchy of layout widgets programatically works the same way as normal programatic creation, except that sizing info needs to be specified in a special way.

    // make a dummy div just to specify size
    
    var div = document.createElement("div");
    with(div.style){ height="500px"; width="500px"; }
    
    // create the layout container
    var lc = dojo.widget.createWidget("LayoutContainer", null, div);
    
    // add some children for top, bottom, and center.  Top and Bottom
    // children also need to have a size specified, and possibly a scrollbar
    var topDiv = document.createElement("div");
    with(topDiv.style){ height="30px"; overflow="auto"; }
    lc.addChild( dojo.widget.createWidget("ContentPane", { href: "foo/bar.html", layoutAlign: "top" }, topDiv) );
    var bottomDiv = document.createElement("div");
    with(bottomDiv.style){ height="30px"; overflow="auto"; }
    lc.addChild( dojo.widget.createWidget("ContentPane", { href: "foo/bar.html", layoutAlign: "bottom" }, bottomDiv) );
    

    One other thing to note in this example is that each ContentPane has two parameters. The href parameter applies to the ContentPane itself, but the layoutAlign parameter is really something that the LayoutContainer processes.

    Doing your own positioning

    You also have the option to lay stuff out on the screen like LayoutContainer does, but without using LayoutContainer. There's a function called dojo.html.layout() that will position a bunch of elements just like LayoutContainer does. (Actually LayoutContainer calls this function.)

    Multiple Renderers

    Different browsers have different capabilities when it comes to displaying (rendering) your widget. Dojo provides mechanisms that automatically detect which of these capabilities the browser offers and extends your widget using the most powerful rendering system the widget has code for.

    We'll discuss how these mechanisms work, when you should use them, and how to extend widgets to support multiple renderers.

    Defining And Extending Widgets with Multiple Renderers

    The section discusses how to code a widget that supports multiple renderers, and how to extend such a widget.

    How to support multiple renderers

    // renderer-agnostic portion

    dojo.declare("my.widget.Foo"

    {

    initializer: function() {

    // do initialization tasks, make instance properties

    },

    foo: 5,

    doit: function() { ... },

    ...

    }

    );



    // render-specific portion

    dojo.widget.defineWidget("my.widget.html.Foo", [ dojo.widget.HtmlWidget, my.widget.Foo], {

    initializer: function() {

    // do initialization tasks, make instance properties

    },

    ...prototypical properties (in object notation)...

    }

    );



    dojo.widget.defineWidget("my.widget.svg.Foo", [ dojo.widget.SvgWidget, my.widget.Foo], {

    initializer: function() {

    // do initialization tasks, make instance properties

    },

    ...prototypical properties (in object notation)...

    }

    );



    Subclassing from multiple renderers

    // renderer-agnostic portion

    // add features to my.widget.Foo, but don't explicitly extend or inherit

    // my.widget.Foo properties will come in as part of my.widget.[html|svg].Foo


    // do initialization tasks, make instance properties

    dojo.declare("my.widget.FooPlus", my.widget.Foo, { ... });



    dojo.widget.defineWidget("my.widget.html.FooPlus", [my.widget.html.Foo, my.widget.FooPlus], {

    initializer: function() {

    // do initialization tasks, make instance properties

    },

    ...prototypical properties (in object notation)...

    }

    );



    dojo.widget.defineWidget("my.widget.svg.FooPlus", [my.widget.svg.Foo, my.widget.FooPlus], {

    initializer: function() {

    // do initialization tasks, make instance properties

    },

    ...prototypical properties (in object notation)...

    }

    );

    Understanding The Widget Hierarchy

    Before you can write your own widget, you should understand how dojo's widgets are organized, both in terms of where files are and how the class hierarchy works.

    Renderer Base Classes

    The first thing to notice is the following renderer base classes.

    Widget 

    |-- DomWidget

    |-- HtmlWidget

    |-- SvgWidget

    Each widget implementation will extend either HtmlWidget?, SvgWidget?, or VmlWidget?, according to what browser it supports.

    Class Hierarchy

    For widgets w/only a single implementation (usually "html"), the class hierarchy is pretty simple. For example, there is a dojo.widget.html.Button that extends HtmlWidget.



    However, widgets w/multiple implementations are more complicated, because there's a base class that defines common functionality and the parameter list for the widget. This effectively leads to multiple-inheritance, since the widget implementation to pull in stuff from both from the renderer base class and the widget base class. For example, dojo.widget.svg.Chart needs to effectively inherit from both SvgWidget and from dojo.widget.Chart.

    Technically, multiple implementations are defined using mixins, which are similar (but subtly different) than multiple-inheritance. In the above case, dojo.widget.svg.Chart extends HtmlWidget but mixes in dojo.widget.Chart base class.

    Directory Structure

    For widgets w/a single implementation, like Button:

    • src/widget/Button.js - defines dojo.widget.html.Button
    For widgets w/multiple implementations, like Chart (in the future):

    • src/widget/Chart.js - defines dojo.widget.Chart base class
    • src/widget/svg/Chart.js - dojo.widget.svg.Chart, svg implementation
    • src/widget/vml/Chart.js - dojo.widget.vml.Chart, vml implementation

    Understanding Widget Renderers

    Implementations

    Widgets can have (but are not required to have) multiple implementations, as follows:

    • svg - will run on any svg enabled browser (FF, later safari, soon other browsers)
    • vml - will run on IE
    • html - can run on any browser

    Note that the so-called "html" version of the widget might actually run special code for IE, FF, etc., either through calls to utility functions (such as the graphics library) that branch based on browser version, or "if/else" statements, or whatever.

    Which widget gets run?

    The HTML file just specifies the widget name, without specifying the implementation. For example,

    <div dojoType="Foo">


    Dojo will pick which version of the widget to run based on the user's browser and what versions of the widget are available. For example, on IE, it will run dojo.widget.vml.Foo if it exists, and otherwise run dojo.widget.html.Foo.

    Modules for Implementations

    There are three separate modules, corresponding to the implementations above:

    • dojo.widget.svg
    • dojo.widget.vml
    • dojo.widget.html

    Examples:

    1. The Button widget only has a single "html" implementation. It's defined in dojo.widget.html.Button



    2. In the future, the Chart widget will have both "svg" and "vml" implementations, defined in dojo.widget.svg.Chart and dojo.widget.vml.Chart. (The widget doesn't instantiate at all on browsers that don't match either svg or vml) Author: the stick

    Why Decouple Code?

    Having an object without any code to display it can be a strange idea for many people. "After all," they say, "I'll only be using my widget in an HTML environment." Such a kneejerk reaction is understandable, as many people view the decoupling of code as an effort not worth the time.

    What you end up with is a method that does some logic, does some rendering, does some more logic, in a fairly long loop. Splitting these processes up is merely saying, "Let's get all of our business logic out of the way, and then we can draw the results." Even if you won't be splitting your widget into multiple files, as we'll be discussing shortly, this should still be done. The next time something isn't displaying correctly, you won't have to wade through business logic to find the problem. The next time business logic isn't working correctly, you won't have to search through display-specific code. And the most important part, you won't worry about modifying business logic or display-specific code breaking the other piece.

    What you should end up with is a plain old JavaScript object that doesn't know about how it will be drawn, and doesn't care. It's the guts of your widget, and can be run in the console, in an SVG environment, in a standard HTML environment, or anywhere that Dojo currently supports or will support in the future.

    When a widget is loaded, it has a lifecycle that runs calls several methods. These methods are pretty clearly separated into business logic and display-specific methods. For example, mixInProperties is business logic and setWidth is display-specific. There is no need for these methods to even interact with each other, and splitting these between multiple files make it easier to locate one from the other.

    You should also end up with most reusable code. Instead of loading external data in the same method as display-specific code, you can move it to your main widget object and call it from the display-specific code. Then, when another method needs to use the same information, it's ready for you to use.

    Navigation

    Need help updating this page. Describe the navigation widgets in Dojo including attributes that are common to all these widgets.



    Menu2

    PopupMenu

    ProgressBar

    ToolBar

    Tree2

    FishEye

    Tree widget

    Introduction

    This documentation refers to 3rd major version of the tree widget, sometimes refered to as TreeV3.

    Many mentioned classes (e.g TreeLoadingController) have V3 on the end, but that suffix is sometimes omitted, because it will be removed in dojo 0.5.

    If there exist 2 same classes, but one with V3 at the end - it's the one you need.

    Examples are given in tests (dojo/tests/widget/treeV3), so you might want to check them first and copy-paste exactly the things you need.



    Please browse the Book,

    then ask questions in dojo-interest list

    If you feel the question private

    or want to contribute

    IRC: Freenode, #dojo by nick [algo]

    ICQ: 820317

    "Ilia Kantor" ilia @ dojotoolkit.org

    Extensions



    Extensions are also called plugins, they can be hooked onto widgets in various combinations and provide wanted options.

    Currently there is a couple of extensions

    TreeDisableWrapExtension

    Tree extension, disables wrapping for tree nodes. Also it fixes IE bug when an 'unwrappable' node (e.g single word) will move to next line if no space left.

    TreeDocIconExtension

    Tree extension, places icon to the left of a node, depening on nodeType property

    TreeEmphaseOnSelect

    Selector extension, highlights currently selected nodes

    TreeDeselectOnDblselect

    Selector extension, deselects a selected node when it is clicked. Usually, one should ctrl-click, or click another node.

    TreeLinkExtension

    Tree extension, turns labels into links, merges object property into tag

    Faq



    How to make tree unselectable?

    To make tree (or its elements) unselectable use dojo.html.disableSelection in nodeCreate and treeCreate hooks. Apply disableSelection to every node you want to make unselectable.

    How to bind an object to tree node?

    There is an "objectId" property and "object" property ready to be filled in from markup or program-way.

    How to walk all node descendants ?

    You may use dojo.lang.forEach(nodeOrTree.getDescendants(),function(elem) { ... }) to process all descendants, it will walk children property recursively.

    The safer way would be to call TreeCommon.prototype.processDescendants(nodeOrTree, filter, func), it will process all children with func, but will not descend into nodes if filter(node) returns false. E.g see collapseAll controller method uses it to collapse all widgets, but skip non-folders and data objects.

    How to evade a situation where all nodes are (re)moved and tree is empty without a way to add new child (no nodes) ?

    Make a single root node with actionsDisabled="DETACH;MOVE". User will be unable to remove it, so interface will stay sane.

    Also, you may want to set actionsDisabled="ADDCHILD" to tree itself, so now children can be added besides the root.

    How to create a custom tree node ?

    First, of course, you may explicitly use createSimple for your widget and declare your widgetType in markup.



    But sometimes, tree has to create a node from data object or just from "nothing", e.g in case of createAndEdit.



    Then it checks for widgetName property of data object (can be namespaced), and if no widgetName, then tree.defaultChildWidget property should contain node class, e.g mycustom.tree.Node.



    Usually, when you override a node, all you need is to adjust defaultChildWidget,

    because widgetName uses generic create and hence works slower right now.



    How to make pages open when a user clicks on node?

    There are 2 ways. The first one is to attach TreeSelector and hook on "select" event. So when a user clicks, event handler will change url to node.object.href. Of course, you should fill hrefs.

    A probably more convinient path would be to employ TreeLinkExtension, which will turn your labelNodes into real links, and apply attrbutes from node object to them.

    I open very large tree. But navigation away to another page from the tree takes time. What's up?

    Dojo performs actions not only when a node is created, but also cleanup when a node is destroyed. Lazy features allow node creation be distributed in time, but when you navigate away from a large tree, large cleanup causes visible delay. I don't know a way to evade that.

    How to add icons to nodes ?

    TreeDocIconExtension handles that. You should declare nodeType for your nodes, so they'll get nodeIcon[Your type] CSS class. Default type is Document for leaves and Folder for folders.

    There is also setNodeTypeClass method to update node CSS when its nodeType changes e.g programmatically.



    Introduction

    Introduction

    This documentation refers to 3rd major version of the tree widget, sometimes refered to as TreeV3.

    Many mentioned classes (e.g TreeLoadingController) have V3 on the end, but that suffix is sometimes omitted, because it will be removed in dojo 0.5.

    If there exist 2 same classes, but one with V3 at the end - it's the one you need.

    Examples are given in tests (dojo/tests/widget/treeV3), so you might want to check them first and copy-paste exactly the things you need.



    Please browse the Book,

    then ask questions in dojo-interest list



    If you feel the question private

    IRC: Freenode, #dojo by nick [algo]

    ICQ: 820317

    "Ilia Kantor" ilia @ dojotoolkit.org

    Features

    Features

    Flexible styling

    • All design in CSS through classes and class combinations
    • Different trees be styled with different CSS class families
    • Multiline and rich content support

    Full set of node operations

    • expand/collapse
    • create with JS or markup
    • destroy/move/clone
    • addChild/detach/(de)folderize
    • inline editing
    • multiple selection and drag'n'drop
    • keyboard controls

    Performance

    • batch operations
    • special features
    • profiled and optimized

    Dynamic node loading & RPC features

    • rich API

    • callbacks & errbacks
    • suited to be in-sync with data
    • locking

    Event system

    • publish

    • hook on any tree change

    Customization

    • change everything through inheritance, events and css
    • out-of-the box extensions
    • coded with it in mind

    Tests and demos

    Tree overall structure

    Note: most classes here omitt 'V3' suffix

    Model + View

    The tree itself is a TreeV3 class instance. Hierarchy is maintained in a standard widgety way: through children[] array. Children are usually TreeNodeV3 instances, but you could use your own(overriding?) implementation of course.



    TreeV3 instance also represents an 'invisible root' node, so it shares common methods with TreeNodeV3. These methods reside in TreeWithNode mixin.



    Model contains data and manipulation methods like "addChild", "detach" .. etc. It also publishes events when modified.

    DOM-structure and view is also merged into model.



    Various functionality can be hooked on model's events: controller, menu, drag'n'drop etc.



    Model events should help you to integrate tree with application on data-level, so you hook on actual data changes, not the cause (program call, user click etc).

    Controller

    Main controllers are TreeBasicController -> TreeLoadingController -> TreeRpcController

    Basically, they are responsible for operating on model and performing most logic, besides model's action. It also makes checks / remote calls.

    Usually, one should work with controller only and let it process model.



    TreeLoadingController and TreeRpcController are known to perform remote calls to server. They use dojo.Deferred and dojo.DeferredList for that purpose.



    Most customizations are also about controller.



    And, by the way, model has no idea about its controller... It throws events and delivers API to call, that's all.

    Extensions

    The stuff is loosely coupled, so a bunch of extensions can be hooked on events too









    What's new in TreeV3

    New HTML/CSS structure

    Nested divs

    Previous tree used a list of divs, each of them was indented with grid and spacers to right level. The new tree uses natural nested divs structure (children' divs inside parent's div). Grid is contigous and structure is displayed correctly for any node/font size

    All design in CSS through classes and class combinations

    All image and size information was removed from JS code. There is a bunch of classes applied to nodes, that may denote node folder state, node type, show if there are children, etc. CSS moves this logical classes into style

    Different trees be styled with different CSS class families

    Want to put 2 differently styled trees on a page? Give them different classPrefix.

    Multiline content support

    Rich content support was incomplete, because list-of-divs model could not handle arbitrary-sized nodes. Now you may have <br>, <p> and any other width/height

    modifiers.

    Event system modified

    nodeDOMCreated event was removed. That's because listeners are bound to tree and may want to modify the new node, but that's only possible when the node is being bound to the tree, not when it was created and hanging around. afterTreeChange was introduced to help listeners to (un)bind nodes the right moment.

    All events were renamed to better reflect the moment of their publishing.

    afterExpand, afterCollapse events now fire when the animation (e.g fading in or out) finishes, not when the actual expand/collapse is called.

    Lazy widget creation

    Before TreeV3, all nodes must be widgets. A node is added - hence graphical widget is created. For performance reasons that behavior was altered. Now when you add a node, you may actually add a "data object", containing node data, e.g {title:"new node"}. You may want to add a large nested branch of such data objects, like {title:"new", children:[...data objects..]}.

    Data objects will become real members of children array (you may recursively search them, modify etc), but graphical widgets will be created only when visitor expands them.

    The compatibility drawback of such behavior is that old code may erroneously call widget methods on data objects while recursively traversing a tree, e.g with Widget#getDescendants. You should change such code to use TreeCommon#processDescendants, or handle data objects in special way.

    There are no special mechanisms to add laziliy instantiated "data objects". You may manipulate them simply modifying children array, but no events are thrown until a real widget appears on the scene. In most cases that is fine, but you are free to "disable" lazy widget creation - do not modify children directly and enable tree.eagerWidgetInstantiation

    Tree extensions

    • Many features were moved from core into extensions
    • Added TreeDocIconExtension instead of builtin childIcon support
    • Selector now only throws events, not doing anything with nodes
    • Out-of-the box extensions introduced to be examples and handle well-known requirements

    Implicit helpers removed

    The Tree is actually a pack of loosely coupled components, connected through events. To keep things simple and also for compatibility reasons, such components(controller,selector...) were created implicitly, if not declared. But actually this proved to be a source of questions and misunderstandings. So now nothing is created implicitly, read how-to and declare things.

    RPC has both sync/async modes

    Old callbacks code was removed in favor to dojo.Deferred. Now all operations may be async and run your callbacks at the end.

    Drag'n'drop changes

    Multiple selection and multiple drag'n'drop (incomplete)

    Sounds simple enough.. Select multiple nodes with ctrl and get them with selector.selectedNodes. instead of removed selectorNode call.

    Currently, multiple drag'n'drop does not work with multiple selection because of dojo bugs. Hopefully will be fixed.

    Drop of any source, not just tree node

    If treeNode property is empty, tree will create a new node from the data returned by source.getTreeNode, then source.onDrop will be called to remove old node.

    Inline node editing

    It became possible to edit nodes inline, using TreeEditor. Base variant uses RichText widget, you can make another wrapper though. Remote calls can be made on save only, or on start/cancel too e.g for locking purposes.

    Author: Ilya

    Node creation

    There are few code paths that lead to same purpose: to create a tree node. They differ in effeciency and use patterns

    Markup creation

    You specity a tree and its nodes in HTML, relying upon dojo to parse it and turn into widgets. That is a slowest way, but nice for small trees or if only tree top is specified and the rest is created later.

    dojo widget parser walks DOM and creates a special structure. The next pass creates widgets from the structure.

    Widget#create

    The generic widget creation routine. It basically runs the operations in order:

    • Mix in widget properties from parameters/markup
    • Register widget in widget.Manager
    • Call buildRendering to make fill template and create domNode
    • Call initialize
    • Call postInitialize. registers widget as a child of its parent and after it creates all subwidgetsCall postCreate

    Note that initialize is called in pre-order: parent is initialized before children, postInitialize is called in post-order: a child is postCreated before its parent.

    Manual creation

    If you create nodes with javascript, then you run create calls manyally. So parents are naturally created (and postCreated) before children.

    There seem to be no good way to distinguish betwen markup creation and manual creation. From the one hand its seems good, because allows reuse of generic creation code. From the other hand code paths going through this code are subtly different.

    The reliable thing is that initialize will process widget after its domNode is built, BUT it should not assume anything about children.

    afterChangeTree event is fired on initialization also. If you want to know anything about children and do something at this point - check addChild, but not node creation.

    Input parameters

    children array may be

    • empty
    • contain widgets, e.g if created from markup, or someone created them before parent and pushed in
    • contain data objects, that will be turned into widgets when parent expands.
    • isFolder comes into play only when there are no children. It allows creation of empty folders, with UNCHECKED state that can be filled later.

    Performance

    Tree was coded with performance in mind. Although, JavaScript itself is a slow language. Flexible model requires some code that slows it down. It's not DOM manipulations, but actually javascript that I couldn't make lighter. Being a part of dojo/widget structure implies some overhead, but also power.

    Almost all operations require small constant time when single node is involved. Depending on your application you may notice slowdown when (most common) creating lots of nodes or performing other batch operations.

    Creation from markup or with standard create/addChild routines is 2-3 times slower, because these routines are generic.

    Comparison

    Fast node creation with dojo tree is 2-3 times slower than xtree 1.7, another tree widget, not so featured, but nicely optimized for performance.

    Important

    The results described here refer to operations without any lazy features involved. Most of time you will use lazy creation or lazy loading, or both, and operate with thousands of "virtual" nodes with ease.

    Performance Tricks

    When talking about performance, one should understand, that there are single-node operations that operate on single node... These ones are fast. The examples are: create a node, delete a node, move a node along the tree.

    ... And there are batch operations that touch a lot of nodes. The examples are: initial tree creation, moving a node from one tree to another which has different listeners, etc.

    That performance issues become noticeable at 100-300 tree nodes depending on your trees. All algorithms are linear in worst case, but JS is slow language, DOM is also not that fast.

    There is a number of features one could use to get a speedup.

    Lazy loading

    A node can be created with isFolder=true flag, but without children. Any node has a state, initially UNCHECKED for empty folder, and used by TreeLoadingController.

    When a user presses expand, tree controller (supporting lazy loading) will send a request to server asking for nodes, and parse the answer creating children.

    The benefit is obvious: you don't have to load/process whole tree at once. You can only load a single node and user will load the rest clicking "expand"

    Lazy creation

    Node/tree keeps array of its children in children property. Lazy creation is somewhat a half-way approach to lazy loading. It allows you to put data objects into this array and tree will create widgets of them later, when they are expanded.

    For instance, one can call node.children = [{title:'node1'},{title:'node2'}]. The objects will be set, but no widgets are created. You can also set children to nested array: node.children = [{title:'node1', children:[{title:'node2'}] }].

    You can create tree on server, JSON-serialize it and put to HTML, that is gzip-compressed. Compression will be 6 times or more, so it is not that space hungry.

    The benefit comes from postponing almost all real job: widget creation and attaching it to tree will happen in expansion-time.

    Comparison between lazy creation and lazy loading

    • You need web-service for lazy loading, not for lazy creation
    • No network waits for lazy creation
    • Lazy creation gives you the tree right here. You can search data objects and modify them without spending time and memory on graphical widgets

    Sometimes, lazy creation and loading may work together nicely, providing seamless increase in speed and decrease in memory footprint. For instance, server may pass a whole tree branch in JSON to lazy loading controller. Top nodes will be created right along, because user needs them, but the rest of the branch will be postponed relying on lazy creation feature.

    There are operations, like "expandAll" where such lazy tricks don't help, because all graphical widgets must be processed. That is why widget creation process is well-optimized itself. createSimple is a hacky program-only way to create TreeNodes fast. setChildren is a method to assign (and create if needed) all children at once. It helps to evade some extra work happening when children are added one by one.

    IE image-reloading fixup (!!!)

    IE has a well-known bug. If an image was loaded dynamically - with a new Image(), or img.src= assignment, or even as a background of a new node, it will not be cached. So every time when you create a node, all needed icons get loaded from server (or requested at least). A possible solution is to put a special div into HTML (adjust src to your path):

    <div style="display:none">
        <!--  IE  has  a  bug:  it  reloads  all  dynamically  resolved  images,  no  matter,  is  it  
        new  Image()  or  CSS  background.  If  you  don't  specify  images  like  that,
        it  will  reload  them  every  time  a  new  node  is  created  -->
        <img  src="../../../src/widget/templates/images/TreeV3/i.gif"/>
        <img  src="../../../src/widget/templates/images/TreeV3/i_half.gif"/>
        <img  src="../../../src/widget/templates/images/TreeV3/expand_minus.gif"/>
        <img  src="../../../src/widget/templates/images/TreeV3/expand_plus.gif"/>
        <img  src="../../../src/widget/templates/images/TreeV3/expand_leaf.gif"/>
        <img  src="../../../src/widget/templates/images/TreeV3/i_long.gif"/>
        <img  src="../../../src/widget/templates/images/TreeV3/document.gif"/>
        <img  src="../../../src/widget/templates/images/TreeV3/open.gif"/>
        <img  src="../../../src/widget/templates/images/TreeV3/closed.gif"/>
    </div>
    
    Author: Ilia

    Server communication



    To talk with server, one should use TreeLoadingControllerV3 or TreeRpcControllerV3. They inherit from TreeBasicControllerV3 and override its methods to deliver remote calls possibility.

    TreeLoadingControllerV3 contains main methods for server calls, and allows dynamic node loading. TreeRpcControllerV3 adds server calls to tree manupulations like "createChild/move/edit...".

    Url settings

    All requests go through dojo.io.bind, usually via XMLHttpRequest transport.

    contains basic Url for all requests, e.g "http://site.com/remoteTreeService.do". You can have query string in it also.

    every call adds special action parameter to query string to distinguish between call types. Actions are move, createChild..

    For children loading, the action is getChildren.

    An example url for such action would be "http://site.com/remoteTreeService.do?action=getChildren".



    Most actions imply additional data parameter with information about node/tree and other action details server may want to know.



    This way of composing an url is described in getRpcUrl, feel free to override if need.



    Request format

    data parameter is JSON-serialized. It usually sends some information about involved nodes and position. If you want to extend it somehow,

    1. override method of controller that corresponds your action, your changes will affect this action only,
    2. override getInfo method of node/tree to affect parameters globally
    3. override getInfo method of controller if that's the right place =)



    Response format

    All data is JSON-serialized. There are libraries for JSON in most of programming languages.



    Server response is evaluated as javascript. That means you can embed any javascript, that will be evaluated on client-side. But it should return object. Use object error property to signalize about server-side error.



    Good answer:

    dojo.debug('I can also put javascript in server answer');

    ([{title:"test",isFolder:true,objectId:"myobj"},{title:"test2",children:[ {title:"test2.1"} ]}])




    Good answer:

    ({})



    Good answer, will return dojo.RpcError

    ({error: "Permission denied"})





    Bad answer format (string), will return dojo.FormatError

    Exception: blabla at line 50



    Transport error (e.g 404) will also return dojo.CommunicationError



    If you don't know what to return, return ({}). That means just "ok". Note outer brackets, they are needed to make sure it evaluates to javascript Object.



    Callbacks and Error handling

    Any request may be performed in synchroneous and asynchronous manner.

    Both of them return dojo.Deferred object, but for synchroneous call, it will be called until next script line.



    You can call deferred.addCallback / deferred.addErrback to add your actions.



    An example of usage would be



    var deferred = loadingController.expandAll(tree);



    // add action when operation finishes successfully

    deferred.addCallback(function() { alert('expanded all nodes!'); });



    // process error

    deferred.addErrback(function(err) { dojo.debugShallow(err); });





    More information about Deferred class and asynchronous programming can be found at http://mochikit.com/doc/html/MochiKit/Async.html (dojo implementation is Mochikit port), http://twistedmatrix.com/projects/core/documentation/howto/async.html (python implementation and a nice state-of-art intro).











    Tree Events

    There are many classes of events, published with dojo.event.publish mechanism. Every event has a name and message object, containing more precise information about what happened. You may use events to update your data while tree changes, and to perform additional processing of involved objects.

    There is a default naming scheme for an event class. E.g for a tree with widgetId='mytree', event of class afterTreeCreate will be named "mytree/afterTreeCreate". You may provide other names in eventNames property of the tree.

    afterTreeCreate

    Event occurs after tree creation is complete. There is an alternative to hook on this action by putting your objects in "listeners" property of the tree. The difference is that listeners are guaranteed to hook before nodes get added, and afterTreeCreate is published after Tree widget is created.

    source

    references to tree

    beforeTreeDestroy

    Published right before actual Tree#destroy method is called. Useful for cleanups

    source

    references to tree

    beforeNodeDestroy

    Right before TreeNode#destroy is called. Node is detached after this event fired.

    source

    references to node

    afterChangeTree

    This event is tightly created with node creation process. It is fired when

    • a node is created

      • no parent at this stage
      • fires in initialize(), so children may be not added yet

    • a node was moved to another tree widget

    oldTree

    references previous tree, null if node has been just created

    newTree

    new(current) tree

    node

    target node

    afterSetFolder

    Fires when a node obtains "folder" state. That may happen when a first child is added to a leaf, or if a node was initially created with isFolder=true

    source

    references to node

    afterUnsetFolder

    Fires when a node obtains looses "folder" state. That may happen when a last child leaves the node, and Tree.unsetFolderOnEmptyis set, or when unsetFolder is called explicitly.

    source

    references to node

    (before|after)Move(From|To)

    These events share same arguments and fire when a node is moved. Move process is considered something special. When you move a node, no detach/addChild events get thrown. That allows to tell situations when a node leaves a tree for some time (detached then attached) from situations when a node is simply moved to another location

    oldParent

    previous parent

    oldTree

    previous tree

    oldIndex

    previous index among siblings

    newParent

    new parent

    newTree

    new tree

    newIndex

    new index among siblings

    child

    target node

    afterAddChild

    Published when a node is attached to parent. This may occur at the end of creation process, or when a node is lazily instantiated from data object.

    Also it occurs when a detached node gets attached.

    child

    references to node

    index

    index among siblings

    parent

    current parent who adopted a child

    childWidgetCreated

    flag is set if child was laziliy instantiated. That is: it resided as data object in children array, but user expanded its parent, so node widget came to life.

    afterDetach

    Occurs when a node is detached. This may happen in the process of node destruction. Keep in mind, that detaching a node sets its parent to null, but

    tree remains same.

    child

    references to node

    parent

    references to old parent

    index

    references to index among children of old parent

    after(Expand|Collapse)

    Fire when a node is expanded/collapsed. Some togglers do nice animation hiding/showing node. This event fires when animation finishes.

    source

    target node

    afterSetTitle

    When a node is edited, or explicit setTitle method is called, this event helps to inform interested parts about changes.

    source

    target node

    oldTitle

    replaced node title

    Tree HTML/CSS model

    There are few major approaches to building dynamic trees.

    1. list of idented divs

    Each tree node is a div with indentation. Indentation is e.g 20px * node depth, so everything looks fine. Usually indentation is made of many quadrantic images, each of them represents empty space or grid lines, which visibly link nodes together.nested divs.

    Of course, 'div' can be changed to any tag, e.g 'li'.

    2. nested divs

    Divs are nested same way tree nodes are nested. Can use ul/li instead of divs, there's only symantic difference, of course, if styles are same.

    Each div can be idented relatively to its parent with padding/margin property, or with images.

    If we use images here, then there will be lots of extra tags, so padding/margin seems better.

    Dojo tree adapts the 2nd approach, of course, with padding/margin identation.

    Let's consider a simple tree

    * Node1

    * Node 1.1

    * Node 1.2

    * Node 2















    (Page is unfinished, and content will probably be merged into the Trees intro page -- CAR)

    Trees

    The trees we see in User Interfaces help sort out long, heirarchical lists. A file system is the classic example, with Windows using it in Explorer and Macintoshes with Finder (is it still called that???).

    Nodes are the basis of a dojo tree. A node can include other nodes, and is then called a branch, container or folder. A node containing no other nodes is a leaf. Dojo does not force you to distinguish branches from leaves. It deduces the tree structure from your own code.

    A dojo tree contains at least two dojo widgets:

    But there are many dojo widgets to help you sculpt, mold, and connect behavior to your tree.

    1 Hello Tree World

    Here's a simple example.

    <div dojoType="Tree" >
      <div dojoType="TreeNode" title="Item 1">
        <div dojoType="TreeNode" title="Item 1.1" ></div>
          <div dojoType="TreeNode" title="Item 1.2" >
             <div dojoType="TreeNode" title="Item 1.2.1" >
                <div dojoType="TreeNode" title="Item 1.2.1.1" ></div>
              </div>
              <div dojoType="TreeNode" title="Item 1.2.2" ></div>
           </div>
        <div dojoType="TreeNode" title="Item 1.3" ></div>
      </div>
      <div dojoType="TreeNode" title="Item 2" ></div>
    </div>
    

    Which produces the following lovely tree:

    SCREENSHOT

    You can do open a node and show its contents by clicking the + icon, or hide them with the - icon, just like you're used to. Nice!

    2 Connecting an Action to a Node

    The problem is our tree does nothing but stand around looking beautiful. Nothing wrong with that. Normally, though, you'd want some kind of action to occur when the node is clicked. To do this, you can use the TreeSelector Widget.

    TreeSelector is a widget without a UI. You use it as a placeholder for connecting the tree to various Javascript actions. This makes it easy to construct many trees, and connect them to the same actions.

    <script>
    	dojo.addOnLoad(function() {
    		dojo.event.topic.subscribe("nodeSelected",
    			 function(message) { alert(message.node.title+" selected"); }
    		);
    	});
    </script>
    
    <div dojoType="TreeSelector" widgetId="tSelector" eventNames="select:nodeSelected" ></div>
    
    <div dojoType="Tree"  selector="tSelector" >
      <div dojoType="TreeNode" title="Item 1">
        <div dojoType="TreeNode" title="Item 1.1" ></div>
      <div dojoType="TreeNode" title="Item 2">
    </div>
    
    (Is there an easier way to do this??? -- CAR)

    When you click on a node, an alert box will pop up with the name you selected.

    3 Submitting a Selected Node

    You can make the selection event arbitrarily complex. But many times, you just want to pass the selected node along with a form. Simple!

    <script>
    dojo.addOnLoad(function() {
       dojo.event.topic.subscribe("nodeSelected",
           function(message) { document.menuForm.eatMe.value = message.node.title; }
       );
    });
    </script>
    
    What would you like to eat first?
    
    <form name="myForm">
      <input type="hidden" name="eatMe" value="" />
      <div dojoType="TreeSelector" widgetId="tSelector" eventNames="select:nodeSelected" />
    
        <div dojoType="Tree" selector="tSelector" >
          <div dojoType="TreeNode" title="Dessert (Recommended)">
            <div dojoType="TreeNode" title="Ice Cream" value="ICE76645" />
            <div dojoType="TreeNode" title="Cake" value="CAK85467" />
         </div>
         <div dojoType="TreeNode" title="Entree">
            <div dojoType="TreeNode" title="Meat Loaf" value="MTL18908" />
         </div>
      </div>
    </form>
    

    Clicking a node fills the value ICE76645, CAK85467, or MTL18908 into the hidden field "eatMe".

    In this example, a tree is a standin for a select/options tag. For long lists, a select/option list gets too long to navigate. Humans like their information grouped and organized into smaller chunks. But databases thrive on flat namespaces, like the UPC system or Social Security Numbers. Trees give you the best of both worlds.

    4 Gridlines

    By default, tree nodes always have a gridline on their left. These gridlines helps the user quickly see which nodes are siblings, and which node is the parent.

    You can turn off gridlines at the root level and/or for the entire tree. By default, each 1st level TreeNode connects to a "phantom" root node, as in:

    SCREENSHOT

    You can remove the phantom Root node so the first level nodes appear with no gridlines to their left, as in:

    <div dojoType="Tree" showRootGrid="false">
    

    Or you can turn all the gridlines off, as in:

    <div dojoType="Tree" showGrid="false" showRootGrid="false">
    

    5 Pre-Expanding Content

    Let's say you'd like to highlight a particular Tree node, for example a default value. You can do this easily enough with tags around the node title:
    <div dojoType="TreeNode"...>
      <div dojoType="TreeNode"...>
        <div dojoType="TreeNode" title="<span style='background-color:yellow' >The most popular choice</span>" />
        ...
      </div>
      <div dojoType="TreeNode"...>
        <div dojoType="TreeNode" title="Another Choice" />
      </div>
    </div>
    
    But if this TreeNode is 3 levels down, the user will have to expand both levels above it. A better way is to pre-expand content levels. This requires the attribute "expandLevel", which means "expand all nodes that are n levels below" If n is more than 1, all levels between 1 and n are expanded, since seeing an expanded node requires seeing an expanded node above. For example, if you added expandLevel="2" to the top TreeNode:
    <div dojoType="TreeNode" expandLevel="2" ...>
      <div dojoType="TreeNode"...>
      ...
    
    then both The Most Popular Choice and Another Choice will appear. But:
    <div dojoType="TreeNode" expandLevel="1" ...>
       <div dojoType="TreeNode" expandLevel="1" ...>
       ...
      </div>
      <div dojoType="TreeNode"...>
    </div>
    
    will only expand Most Popular Choice.

    Widget Namespaces

    Overview

    Widgets are combined into groups called namespaces. All the widgets built into Dojo are in the "dojo" namespace, but someone else could write their own widgets and put them in a different namespace. For example, you could write your own button and checkbox widgets, and put them into an "acme" namespace. Then "acme:Button" would be your button, and would be unrelated to the button object built into dojo, called "dojo:Button".

    Usage

    Defaults have been chosen to reduce boilerplate. A namespace maps to a top-level module by default. A top-level module path defaults to dojo/../, and widgets are expected to be in .widget.



    Given

    <img dojoType="acme:Image" />
    acme widgets are expected to be in acme folder next to dojo folder.

    acme.widget
    module is expected to contain the Image widget.

    /dojo

    /acme/

    /acme/widget/Image.js <- defines acme.widget.Image
    Loading acme.widget.Image module is the only requirement for using acme:Image in this configuration. You can load that module as part of a build, by calling dojo.require, or automatically.



    To use a folder location other than ../acme call dojo.registerModulePath.



    To select a widget module other than acme.widget, call dojo.registerNamespace.

    Automatic Loading

    To allow automatic loading of widgets in a namespace, include a manifest file. For the example above, the default resource for the manifest would be:

    <root>/acme/manifest.js
    To customize the folder location of module acme call dojo.registerModulePath.



    For most users employing the auto-require system, the manifest file contains a call to dojo.registerNamespaceResolver.



    A namespace resolver tells Dojo what module to load for a named widget.

      dojo.provide("acme.manifest");

    dojo.require("dojo.string.extras");



    dojo.registerNamespaceResolver("acme",

    function(name){

    return "acme.widget."+dojo.string.capitalize(name);

    }

    );
    The input string name will always be lower-case. So this resolver triggers loading of module acme.widget.Calendar for widget acme:calendar.



    The load-time module is not necessarily the same as the widget class module. For example, the acme.widget.Calendar class might be loaded via acme.widget.allWidgets.



    The resolver tells dojo the module to require to load a widget.



    To select a widget class module other than acme.widget, call dojo.registerNamespace.




    API

    dojo.registerModulePath(module, path): maps a module name to a path (formerly setModulePrefix).



    An unregistered module is given the default path of ../, relative to Dojo root. For example, module acme is mapped to ../acme. If you want to use a different module name, use registerModulePath.



    dojo.registerNamespace(namespace, widget_module [, resolver]): maps a module name to a namespace for widgets, and optionally maps widget names to modules for auto-loading.



    An unregistered namespace is mapped to an eponymous module. For example, namespace acme is mapped to module acme, and widgets are assumed to belong to acme.widget. If you want to use a different widget module, useregisterNamespace.



    dojo.registerNamespaceResolver(namespace, resolver): a resolver function maps widget names to modules, so the widget manager can auto-load needed widget implementations.



    The resolver provides information to allow Dojo to load widget modules on demand.When a widget is created, a namespace resolver can tell Dojo what module to require to ensure that the widget implementation code is loaded.



    The input string in the name argument will always be lower-case.

      dojo.registerNamespaceResolver("acme",

    function(name){

    return "acme.widget."+dojo.string.capitalize(name);

    }

    );

    Examples

    Let's say we have a Dojo install at root:

    /dojo/dojo.js

    /dojo/[whatever else is in the particular dojo install]
    We want to create custom modules, and decide to put them in:

    /acme
    Note that the path to acme from dojo is:

    ../acme
    For the widget examples, let's say we made some custom widgets, including one called acme.widgets.Calendar, and put them in:

    /acme/widgets/variousWidgets.js

    Automatic Loading

    Main document

    <script src="/dojo/dojo.js"></script>

    <script>

    dojo.require("dojo.widget.*");

    </script>

    Include a manifest file: /acme/manifest.js

    dojo.provide("acme.manifest");

    dojo.registerNamespaceResolver(function(name) {

    return "acme.widgets.variousWidgets";

    });

    To support markup like so:

    The acme namespace triggers require of acme.mainfest. The resolver is used to match calendar to a required module (i.e. acme.widgets.variousWidgets). Then acme.widgets module is searched for calendar implementation matching the current rendering environment.

    Explicit Loading

    Main document

    <script src="/dojo/dojo.js"></script>

    <script>

    dojo.require("acme.widgets.variousWidgets");

    </script>

    Supports markup like so:

    acme.widgets module is searched for calendar implementation matching the current rendering environment.

    Non-Widget Resources

    Main document
    <script src="/dojo/dojo.js"></script>

    <script>

    dojo.require("acme.lib");

    </script>

    acme/lib.js file:

    // ... additional code ...

    Builds

    With a build you can use any of these formats, but a manifest is not required.

    Main document

    <!-- dojo.js is a build -->

    <script src="/dojo/dojo.js"></script>

    <!-- dojo.require(s) can be here, although they are ignored -->

    Supports markup like so:

    <div dojoType="acme:calendar"></div>

    Writing Your Own Widget

    This section discusses the internals of widgets, and how to write your own.

    Compound Widgets

    TODO: moved this from "The Memo" page, where it definitely didn't belong, but it could use some expansion

    This is a crucial next step for widget authors - creating widgets which themselves contain inner widgets, resulting in what we could call 'compound widgets'.

    The procedure is simple, just add to your widget .js file, within the widget atributes object, the line:

            widgetsInTemplate:true,

    then, your subwidgets will nest perfectly within the main outer widget. You should also be able to nest to any arbitrary depth. Just remember though to abstain from setting the id or dojoId attributes in your html, rather set dojoAttachPoint instead to insert into your main outer widget a named attribute which references your subwidget. This way, you won't pollute the global element namespace. Otherwise, you'll hit problems if creating multiple instances of your compound widgets.

    The Monolithic App Widget

    One approach to dojo application design is to build the app as one huge compound uber-widget, containing all the needed sub-widgets.



    This approach will likely have a natural feel and appeal to those experienced in desktop GUI programming.



    If you want to go in this direction, then your app can get loaded in the client by a very minimal HTML file which just pulls in a minimal stylesheet, includes dojo.js, dojo.require()s your main app widget, then invokes that widget in a single
    tag within your document . There are those of us who feel that the less javascript code you have within html files, the better!

    Custom Namespace

    TODO:

    - this is old info? current description is at http://dojo.jot.com/WikiHome/Modules%20%26%20Namespaces

    - As indicated below, it is correct for the current stable version (0.3.1). It will need to be updated for the next release.

    If you're planning on creating your own widgets then it's probably a good idea to keep your own code completely separate from the Dojo codebase. This will make life easier if/when you come to install a new version of Dojo, and also prevent any name clashes with native Dojo widgets.



    First of all, you'll want to create a directory structure outside the Dojo source directory where your code will be stored. For example, let's call this new directory 'user', so that your directory structure looks something like this:



    	/dojo
    	/user
    	index.html



    Next you need to tell Dojo that this new namespace exists and where it lives. You can do this with dojo.setModulePrefix( namespace, path ), like this:



    	dojo.setModulePrefix("user", "../user");



    Note that the path (the second parameter) is relative to the root of the dojo source directory.



    [Please note: the use of dojo.setModulePrefix() is deprecated (by Dojo version 0.5), and will be replaced with dojo.registerModulePath(), which takes the same initial parameters.]



    Now since Dojo will look for widgets in a subdirectory (under '/user') called 'widget', we need to create that too:



    	/dojo
    	/user
    		/widg