.. _quickstart/writingWidgets: Writing Your Own Widget ======================= :Status: Contributed :Version: 1.0 :Authors: Bill Keese .. contents:: :depth: 2 It's hard for you to leave well-enough alone. We give you widgets, and now you want to change them. Or you want to make your own. No problem! Dijit components are extendible, so you can make changes without touching the source code. In a way, you already do this by specifying your own attributes - e.g. sliders that go from 0-100 look different than those going from 0-200. But sometimes you need to go further. Maybe you need to create different behavior for onClick, or substitute a custom validation routine. This kind of modification uses extension points described in Common Attributes. You can add your own code to extension points through markup or through pure JavaScript calls to dojo.declare. You can also create Dijit classes from scratch. Again, you can do this either through markup - using the dijit.Declaration dojoType attribute - or through dojo.declare. ===================== Starting From Scratch ===================== Let's look at how to create a widget from scratch. Technically, a widget can be any javascript "class" that implements a constructor taking parameters and a srcNodeRef (a pointer to a source DOM node). .. code-block:: javascript constructor: function(params, srcNodeRef){ console.log("creating widget with params " + dojo.toJson(params) + " on node " + srcNodeRef); } However, all the widgets in dijit and dojox, are built on top of the :ref:`dijit._Widget ` base class. The simplest widget you can create is a *behavioral* widget, i.e., a widget that just uses the DOM tree passed into it rather than creating a DOM tree. .. code-example:: :djConfig: parseOnLoad: false .. javascript:: :label: The widget definition .. html:: :label: Instantiate the widget in markup hi This is merely creating a javascript object (of type MyFirstBehavioralWidget) associated with the in the original markup. You would create a postCreate() method referencing this.domNode that did connections, etc. to do something interesting w/that DOM node. This kind of behavioral widget is useful in some cases, but it has severe limitations, namely that the widget user must supply a DOM tree. Normally, widgets create their own DOM tree, replacing a simple or  count: 0 Note that the template should have a single top level root node. Next, we modify the template above with some commands for _Templated: .. code-block:: html
 count: 0"
dojoAttachPoint and dojoAttachEvent are documented in detail on the :ref:`dijit._Templated ` page, but the important thing to note is that dojoAttachEvent sets up a listener for events on the DOM nodes, and dojoAttachPoint sets up a pointer to the DOM nodes. So, putting that all together the source becomes: .. code-example:: :djConfig: parseOnLoad: false .. javascript:: .. html:: press me ========== Attributes ========== All widgets have attributes that can be set on widget creation, or changed during the use of the widget, much like DOM nodes have attributes. The main difference is that to get/set widget attributes after creation, you need to call the attr() method. But how do you as a widget writer make your widget have attributes, and handle when the caller changes their value? Declaring attributes -------------------- As a widget writer, you need to declare all your widget parameters in the prototype, along with a value. The value serves both as a default value (if no value was specified on instantiation), and also tells the parser the data type of the parameter. In this case we are declaring a string parameter: .. code-block:: javascript // label: String // Button label label: "push me" .. code-block:: javascript // duration: Integer // Milliseconds to fade in/out duration: 100 .. code-block:: javascript // open: Boolean // Whether pane is visible or hidden open: true Note that all the documentation for an attribute needs to go next to the attribute definition, even when you need special documentation about how attr() performs for that widget. For example: .. code-block:: javascript // value: Date // The date picked on the date picker, as a Date Object. // When setting the date on initialization (ex: new DateTextBox({value: "2008-1-1"}) // or changing it (ex: attr('value', "2008-1-1")), you can specify either a Date object or // a string in ISO format value: new Date() attributeMap ------------ Often widget attributes are mapped into the widget's DOM. For example, a TitlePane has a "title" parameter which becomes the innerHTML of the TitlePane.titleNode DOM node (where titleNode is defined as a dojoAttachPoint, see above). You might think that that mapping would be specified inside of the widget's template, but actually it's specified in something called the "attributeMap". attributeMap can map widget attributes to DOM node attributes, innerHTML, or class. That explanation is confusing, but an example will help. Here's a simple widget for displaying a business card. The widget has 3 parameters: * name * phone number * CSS class name to apply to name Each parameter is specified in the attributeMap to say how it relates to the template: .. code-example:: :djConfig: parseOnLoad: false .. javascript:: .. css:: .. html:: Also note how the first example uses the default value of nameClass whereas the second example uses a custom value. We could also have made a parameter called "class", and mapped it to this.domNode. Note though that you need to put quotes around the name as it's a reserved word in javascript. To map a widget attribute to a DOM node attribute, you do: .. code-block :: javascript attributeMap: { disabled: {node: "focusNode", type: "attribute" } }, or alternately just .. code-block :: javascript attributeMap: { disabled: "focusNode" }, Both code blocks copy the widget's "disabled" attribute onto the focusNode DOM node in the template. Custom setters/getters ---------------------- When you have an attribute where setting/getting it is more complicated than attributeMap can handle, you need to write custom getters/setters for it. The naming convention (for an attribute named foo) is _setFooAttr() and _getFooAttr(). attr() will automatically detect and call these custom setters. Here's an example of a behavioral widget (it uses the DOM node from the supplied markup) that has an "open" attribute that controls whether the widget is hidden or shown: .. code-example:: :djConfig: parseOnLoad: false .. javascript:: .. html:: This pane is initially hidden Custom setters are quite common. Usually you don't need a custom getter (as the default action for attr('foo') is to access Widget.foo), but for something like Editor where it's impractical to constantly keep Editor.value up to date, writing a custom _getValueAttr() accessor makes sense. Life cycle ---------- The custom setters listed above, plus every attribute listed in attributeMap, is applied during widget creation (in addition to whenever someone calls attr('name', value)). Note that the application happens after ``buildRendering()`` but before ``postCreate()``, so you need to make sure that none of that code is dependent on something that happens in postCreate(), or later. This in particular is an issue for any widgets that depend on timeouts for setup, which need to have special code to handle when _setDisabledAttr() etc. is called during startup. eg: .. code-block :: javascript :linenos: dojo.declare("my.Thinger", dijit._Widget, { value:9, buildRendering: function(){ this.inherited(arguments); this.multiplier = 3; }, _setValueAttr: function(value){ this.value = value * this.multiplier; } }); Had the ``multiplier`` member been defined in ``postCreate``, the initial automated value setting done by attr() would fail. ========== Containers ========== Often a widget declared in markup will have contents, i.e. it will contain some other DOM. For example: .. code-block:: html In the common case of non-behavioral widgets (that create a new DOM tree to replace the ========================= Creating extension points ========================= Let's say you've written a widget, and when the user clicks on it, something happens. What you want is for the programmer using the widget to be able to either *change* what happens, or have something happen in addition, without having to edit your widget. To see how to do this, let's see how dijit.form.Button does it for clicking. Note that we need to distinguish between DOM events, which happen on DOM elements; and widget events, which fire when things happen in the widget. (To make this clearer: DOM onclick might fire on elements in your widget, but you would only want the widget's onClick (Note: camelCase!) to fire when your widget is an "enabled" state.) 1. In your template html, on the html elements you want to have fire DOM events, add the attribute dojoAttachEvent as follows. Here's some of the dijit Button's Button.html (with ... where I've left stuff out): .. code-block :: html
press me or alternately this: .. code-block :: html
press me
Now, whenever someone in the browser clicks on the widget (ok, specifically inside it's top-level div in this case), _onButtonClick and _onClick will execute, but so will the extra alert() statement. 3a. What if you don't want to override the extension point, but want it to execute and then have something custom execute? Just use type="dojo/connect" instead of type="dojo/method". Closing words: 1) Despite the name of the attribute "event", as in event="onClick", it's value is not a DOM event. Remember: onClick is just a plain old js method. (Dojo is misleading here). 2) How can you find the plain old js methods to override or "connect" to (in the dojo sense of dojo.connect)? Well, that can be painful. First, you have to look inside the widget. Or inside its ancestors/superclasses. Or theirs. Or theirs. Not fun. Second, they aren't named consistenly. Sometimes _ means private, sometimes it means protected. (TODO: move to separate page?) ============================= Useful self-scoping functions ============================= There are two sets of functions available to all widgets which simplify connections with other widgets an DOM nodes: * connect/disconnect * ``New in 1.4`` subscribe/unsubscribe These functions operate similar to their dojo.* counterparts - with two exceptions. First, the target function will always be executed within the scope of the widget. Second, these connections/subscriptions will be cleaned up during the destroy() lifecycle phase of the widget. ============= Accessibility ============= These pages list how to make your widgets accessible to people with poor/no vision, etc. * :ref:`Creating Accessible Widgets ` * :ref:`Testing Widgets for Accessibility ` === DTL === There's an alternate template syntax for widgets which lets you have conditional code in templates and other advanced features. * `DTL manual from 1.2 `_ * :ref:`DTL ` (currently the top page from the above link has been copied to :ref:`DTL_cur `) ======== See also ======== * :ref:`Declaring a widget in markup ` * Widgets in templates are discussed on the :ref:`dijit._Templated ` page * :ref:`Example: File Upload Dialog Box ` * :ref:`Dropdowns and Popups ` * `Intro to behavioral and templated `_ * `The Memo `_ - doc about writing a simple widget from scratch