.. _releasenotes/migration-2.0:
===============================
Dojo 1.x to 2.0 migration guide
===============================
**Note that because of substantial changes to the JS ecosystem (ES6, TypeScript, reactive programming, etc.), and the changes coming in Dojo 2, *this guide is no longer accurate*.**
The migration from Dojo 1.x to 2.0 will not be trivial. We suggest two primary co-existence approaches with a gradual migration:
* Start adding Dojo 2.0 features into a Dojo 1.x application
* Create a new Dojo 2.0 application, and leverage some Dojo 1.x widgets and features as modules.
We strongly suggest learning the features of ES6 and TypeScript. And we suggest starting with the tutorials found on the Dojo 2 website at https://dojo.io/tutorials/ .
.. contents::
:depth: 3
Currently, many parts of Dojo 2.0 are under development. As with any major software that is under-development, it is
hard to predict that the final solution will look like. These notes provide guidance though on how to try to "future
proof" your application to make it easier to transition to Dojo 2.0 when it release.
Since Dojo 1.X is backwards compatible with previous Dojo 1.x releases, none of these changes are necessary until Dojo
2.0, but refactoring your code earlier will not only make future porting easier, but also can deliver performance and
code maintenance benefits early.
AMD
===
Dojo has been upgraded to use the Asynchronous Module Definition (AMD) standard for all of its modules. This changes
the way you load, access, and define modules.
Loading dojo.js
---------------
Dojo is loaded basically in the same way as before, except that in the ``
If configuration options are specified outside of the ``
Now the following is used: ``
jsId
----
The `jsId` attribute has been removed. Replace all `jsId` references with `data-dojo-id`, the behavior is identical.
.. html ::
I am exported to window.bar by reference
Query
-----
``dojo/query`` is a new module similar to the old ``dojo.query()`` function. In general you can use it like
``dojo.query()``, so old code like:
.. js ::
dojo.query("li").connect("onclick", callback)
can been replaced by:
.. js ::
require(["dojo/query"], function(query){
query("li").on("click", callback);
});
Points of caution:
1. As before, you need to require certain NodeList extension modules to get added methods on the NodeList return
from ``query()``. The difference is that now the NodeList DOM functions also need to be explicitly loaded. So you
need to do:
.. js ::
require(["dojo/query", "dojo/NodeList-dom"], function(query){
query("li").style("display", "none");
});
2. ``query()`` can load various selector engines. By default it uses the ``dojo/selector/light`` engine. If you have
complicated queries you need to switch it to use a more powerful engine. See :ref:`dojo/query ` for
details.
There are a couple of ways to set the selector engine. First, we can define the selector engine as part of the dojo
configuration for the whole page:
.. html ::
You can also specify the selector engine level you are dependent on for each of your modules. This is done by
indicating the CSS selector engine level after ``!`` in the ``dojo/query`` module id. For example, if your module
needed to do a CSS3 level query, you could write:
.. js ::
define(["dojo/query!css3"], function(query){
query(".someClass:last-child").style("color", "red");
});
Stores
------
The :ref:`dojo.data ` API stores have been replaced with the new :ref:`dojo/store ` API.
================================ =================================
dojo.data dojo/store
================================ =================================
store.getValue(item, "foo") item.foo
store.getLabel(item) item.label
store.getItemByIdentifier(id) store.get(id) returns Deferred
store.fetch(...) store.query() returns Deferred
================================ =================================
In order to aid transition, there are two modules that are available:
* :ref:`dojo/store/DataStore ` - Can convert a legacy ``dojo.data`` API store and make it appear
to be a native ``dojo/store``.
* :ref:`dojo.data.ObjectStore ` - Wraps a ``dojo/store`` API store and makes it appear to be a
legacy ``dojo.data`` store.
Many Dijits are now directly ``dojo/store`` aware, including: :ref:`dijit/form/ComboBox `,
:ref:`dijit/form/FilteringSelect `, and :ref:`dijit/Tree `.
Declaring Classes
-----------------
``dojo.declare()`` has been migrated to :ref:`dojo/_base/declare `. There may be further changes
for Dojo 2.0, for example replacing it by ComposeJS, or may have more modest changes. For now, for classes you don't
need in the global scope, you should declare them as baseless. Something like this:
.. js ::
dojo.provide("package.myClass");
dojo.require("dijit._Widget");
dojo.declare("package.myWidget", [dijit._Widget], {
// myWidget Class declaration
});
Should change to something like this:
.. js ::
define(["dojo/_base/declare", "dijit/_WidgetBase"],
function(declare, _WidgetBase){
return declare([_WidgetBase], {
// myWidget Class declaration
});
});
Notice the omission of the first argument in the ``declare()``. This means that nothing will be set in the global
scope. Also, the mixin array uses the return values of the define requirement array, instead of the legacy class
names. This means that your custom class will only be available within the closure scope of a ``require()`` or
``define()`` that has required it in.
This does mean your module can only return a single public class, which is more consistent with the concepts of AMD and
baseless anyways, but if you need to create a private class that isn't referenced outside the current module, you can
simply declare it as a variable. For example:
.. js ::
define(["dojo/_base/declare", "dijit/_WidgetBase"],
function(declare, _WidgetBase){
var _myMixin = declare(null, {
// _myMixin Class private declaration
});
return declare([_WidgetBase, _myMixin], {
// myWidget Class
});
});
FX
--
The base FX features of ``dojo/dojo.js`` have been moved to :ref:`dojo/_base/fx ` and the additional
features of the ``dojo.fx`` module are now in :ref:`dojo/fx `.
================================================= ============================ ====================================
1.x syntax 2.0 module 2.0 syntax
================================================= ============================ ====================================
dojo._Line dojo/_base/fx baseFx._Line
dojo.Animation dojo/_base/fx baseFx.Animation
dojo._fade dojo/_base/fx baseFx._fade
dojo.fadeIn dojo/_base/fx baseFx.fadeIn
dojo.fadeOut dojo/_base/fx baseFx.fadeOut
dojo._defaultEasing dojo/_base/fx baseFx._defaultEasing
dojo.animateProperty dojo/_base/fx baseFx.animateProperty
dojo.anim dojo/_base/fx baseFx.anim
================================================= ============================ ====================================
Promises and Deferreds
----------------------
``dojo.Deferred`` and ``dojo.when`` have been replaced with ``dojo/promise``, ``dojo/Deferred`` and ``dojo/when``.
The functionality in ``dojo.
DeferredList`` has been replaced by ``dojo/promise/all`` and ``dojo/promise/first``.
Old code like:
.. js ::
var d = new dojo.Deferred();
d.addCallback(function(result){
// handle success
});
d.addErrback(function(err){
// handle failure
});
d.callback({ success: true });
d.errback({ success: false });
Should be refactored like:
.. js ::
require(["dojo/Deferred"], function(Deferred){
var d = new Deferred();
d.then(function(result){
// handle success
}, function(err){
// handle failure
});
d.resolve({ success: true });
d.reject({ success: false });
});
The following table provides a quick reference to the changes:
================================================= ============================ ====================================
1.x syntax 2.0 module 2.0 syntax
================================================= ============================ ====================================
dojo.Deferred dojo/Deferred Deferred
dojo.when dojo/when when
dojo.DeferredList([...]).then(...) dojo/promise/all all([...]).then(...)
dojo.DeferredList([...], true).then(...) dojo/promise/first first([...]).then(...)
================================================= ============================ ====================================
XHR and IO Requests
-------------------
``dojo.xhr*`` and ``dojo.io.*`` have been replaced with :ref:`dojo/request `.
Old code like:
.. js ::
dojo.xhrGet({
url: "something.json",
handleAs: "json",
load: function(data){
// do something
},
error: function(e){
// handle error
}
});
Should be refactored as:
.. js ::
require(["dojo/request"], function(request){
request.get("something.json", {
handleAs: "json"
}).then(function(data){
// do something
}, function(e){
// handle error
});
});
``dojo.io.script`` is replaced by :ref:`dojo/request/script ` and ``dojo.io.iframe`` is
replaced by :ref:`dojo/request/iframe` and operate in a similar fashion to the base ``dojo/request`` module.
Note that ``dojo/request`` utilises the new ``dojo/promise`` modules.
Miscellaneous
-------------
================================================= ============================ ====================================
1.x syntax 2.0 module 2.0 syntax
================================================= ============================ ====================================
dojo.window dojo/window window
dojo.Color dojo/_base/Color Color
dojo.cookie dojo/cookie cookie
dojo.date.locale dojo/date/locale
dojo.date.stamp dojo/date/stamp
dojo.date dojo/date
dojo.dnd.* dojo/dnd/*
dojo.hash dojo/hash hash
dojo.html dojo/html html
dojo.currency dojo/currency currency
dojo.number dojo/number number
dojo.string dojo/string string
dojo.Stateful dojo/Stateful Stateful
dojo.window.* dojo/window window.*
dojo.config dojo/_base/config config
dojo.back.* dojo/hash see :ref:`dojo/hash ` reference doc
================================================= ============================ ====================================
Dijit
=====
Mapping table for dijit
-----------------------
This is a quick lookup table for methods, attributes, etc. in 1.x mapped to their equivalent method in that module in
2.0. Note that many methods that were previously included automatically now need to be explicitly loaded.
The sections underneath this give more detail on conversions.
================================================= ============================ ====================================
1.x syntax 2.0 module 2.0 syntax
================================================= ============================ ====================================
dijit.hasDefaultTabStop dijit/a11y a11y.hasDefaultTabStop
dijit.isTabNavigable dijit/a11y a11y.isTabNavigable
dijit._getTabNavigable dijit/a11y a11y._getTabNavigable
dijit.getFirstInTabbingOrder dijit/a11y a11y.getFirstInTabbingOrder
dijit.getLastInTabbingOrder dijit/a11y a11y.getLastInTabbingOrder
dijit.byId dijit/registry registry.byId
dijit.byNode dijit/registry registry.byNode
dijit.registry.toArray dijit/registry registry.toArray
dijit.registry.forEach() dijit/registry array.forEach(registry.toArray(), ...)
dijit.registry.filter() dijit/registry array.filter(registry.toArray(), ...)
dijit.registry.map() dijit/registry array.map(registry.toArray(), ...)
dijit.registry.every() dijit/registry array.every(registry.toArray(), ...)
dijit.registry.some() dijit/registry array.some(registry.toArray(), ...)
dijit.registry.byClass("dijit.form.Button") dijit/registry array.filter(registry.toArray(), function(widget){ return widget.constructor === require("dijit/form/Button"); })
dijit.findWidgets dijit/registry registry.findWidgets
dijit.getEnclosingWidget dijit/registry registry.getEnclosingWidget
dijit.focus dijit/focus focus.focus
dijit.registerWin dijit/focus focus.registerIframe
dijit._curNode dijit/focus focus.curNode
dijit.getFocus() dijit/focus focus.curNode (points to node not hash)
dijit._activeState dijit/focus focus.activeStack
dojo.require("dijit.sniff") dojo/uacss require dojo/uacss instead
dojo.subscribe("focusNode",cb) dijit/focus focus.watch("curNode",cb)
dojo.subscribe("widgetBlur",cb) dijit/focus focus.on("widget-blur",cb)
dojo.subscribe("widgetFocus",cb) dijit/focus focus.on("widget-focus",cb)
dijit.getViewport dojo/window window.getBox
dijit.placeOnScreen dijit/place:place.at
dijit.placeOnScreenAroundElement(n,an,{BL: "TL" dijit/place place.around(n,an,["before","after"])
dijit.typematic dijit/typematic typematic
dijit.popup.open({orient:{BL: "TL",...}) dijit/popup popup.open({orient:["before","after"]})
dijit.hasWaiRole(node, role) node.getAttribute("role")==role
dijit.getWaiRole(node) node.getAttribute("role")
dijit.setWaiRole(node, role) node.setAttribute("role", role)
dijit.removeWaiRole node.setAttribute(role, "")
dijit.hasWaiState("selected") node.hasAttribute("aria-selected")
dijit.getWaiState("describedby") node.getAttribute("aria-describedby")
dijit.setWaiState("describedby", desc) node.getAttribute("aria-describedby", desc)
dijit.removeWaiState("selected") node.removeAttribute("aria-selected")
dijit.layout.marginBox2contentBox dijit/layout/utils utils.marginBox2contentBox
dijit.layout.layoutChildren dijit/layout/utils utils.layoutChildren
dojo.connect(myWidget, "onClick", cb) myWidget.on("click", cb)
dojo.connect(myWidget, "onChange", cb) myWidget.watch("value", function(name, o, n){...})
myWidget.setAttribute(name,val) myWidget.set(name,val)
myWidget.attr(name) myWidget.get(name)
myWidget.attr(name,val) myWidget.set(name,val)
myWidget.attr(hash) myWidget.set(hash)
myWidget.getDescendants myWidget.getChildren
myWidget.setDisabled(bool) myWidget.set("disabled", bool)
myWidget.setValue(val) myWidget.set("value", val)
myWidget.getValue() myWidget.get("value")
myWidget.getDisplayedValue() myWidget.get("displayedValue")
myWidget.setDisplayedValue(val) myWidget.set("displayedValue", val)
myWidget.setLabel(label) myWidget.set("label", label)
myWidget.setChecked(val) myWidget.set("checked", val)
myWidget.setHref() myWidget.set("href", ...)
myWidget.setContent() myWidget.set("content", ...)
dojo.connect(myCalendar, "onValueSelected", ...) myCalendar.watch("value", ...)
Editor.focusOnLoad dijit/Editor perform manually
Editor.blur() dijit/Editor focus something else
dijit._editor.escapeXml() dijit/_editor/html html.escapeXml()
Editor.getNodeHtml() dijit/_editor/html html._getNodeHtml()
Editor.getNodeChildrenHtml() dijit/_editor/html html.getNodeChildrenHtml()
ProgressBar.progress dijit/ProgressBar ProgressBar.value
ProgressBar._setIndeterminateAttr(true) dijit/ProgressBar ProgressBar.set("value", Infinity)
ProgressBar._setIndeterminateAttr(false) dijit/ProgressBar ProgressBar.set("value", 123)
TitlePane.setTitle(title) dijit/TitlePane TitlePane.set("title", title)
Tooltip.addTarget() dijit/Tooltip Tooltip.set("connectId", ...)
Tooltip.removeTarget() dijit/Tooltip Tooltip.set("connectId", ...)
Tree.store dijit/Tree specify Tree.model instead
Tree.query dijit/Tree pass query to Tree.model instead
Tree.label dijit/Tree pass label to ForestStoreModel instead
Tree.childrenAttr dijit/Tree pass to model
Tree.mayHaveChildren dijit/Tree specify on model
Tree.getItemChildren dijit/Tree specify on model
_KeyNavContainer.startupKeyNavChildren dijit/_KeyNavContainer remove call to method
Form.execute dijit/form/Form Form.submit
Form.getValues() dijit/form/Form Form.get("value")
Form.setValues(val) dijit/form/Form Form.set("value", val)
Form.isValid() dijit/form/Form Form.get("state")
dijit._setSelectionRange dijit/form/_TextBoxMixin _TextBoxMixin._setSelectionRange
dojo.connect(myForm, "onValidStateChange", cb) dijit/form/Form myForm.watch("state", function(name, o, n){...})
dijit._Widget replaced widgets use dijit/_WidgetBase
dijit._Templated replaced widgets use dijit/_TemplatedMixin, dijit/_WidgetsInTemplate instead
dijit.form.Slider replaced widgets use dijit/form/HorizontalSlider, VerticalSlider, etc.
dijit.layout.LayoutContainer replaced widgets use dijit/layout/BorderContainer
dijit.layout.SplitContainer replaced widgets use dijit/layout/BorderContainer
dijit._Calendar replaced widgets use dijit/Calendar
dijit.layout.AccordionPane replaced widgets use dijit/layout/ContentPane
layoutAlign="top" widget parameters region="top"
dojoAttachPoint templates data-dojo-attach-point
dojoAttachEvent templates data-dojo-attach-event
waiRole="button" templates role="button"
waiState="selected-false,haspopup-true" templates aria-selected="false" aria-haspopup="true"
attributeMap:{foo:a,bar:b} widget definitions _fooSetter:a, _barSetter:b (NB: in 1.8, _setFooAttr and _setBarAttr)
_setFooAttr:... widget definitions _fooSetter:... (NB: in 1.8, it's still _setFooAttr)
this._focused widget definitions this.focused
this._supportingWidgets.push(...) widget definitions this.own(...)
this.connect(node, "onclick", "myMethod") widget definitions this.own(on(node, "click", lang.hitch(this, "myMethod")))
this.connect(obj, func, "myMethod") widget definitions this.own(aspect.after(obj, func, lang.hitch(this, "myMethod"), true))
this.subscribe(topicName, "myMethod") widget definitions this.own(topic(topicName, lang.hitch(this, "myMethod"))) but note that arguments to myMethod are passed as varargs not array
================================================= ============================ ====================================
set(), get()
------------
Old widget methods to set and get parameter values, such as:
.. js ::
myEditor.getValue()
myTextBox.attr("value")
myForm.setValue(...);
have been replaced by the standard ``set()`` and ``get()`` methods:
.. js ::
myEditor.get("value")
myTextBox.get("value")
myForm.set("value", ...);
watch(), on()
-------------
Old widget methods to monitor widget events or changes in widget parameters have been consolidated to use ``on()`` and
``watch()``:
Old code:
.. js ::
dojo.connect(myForm, "onValidStateChange", function(){ ... });
dojo.connect(myButton, "onClick", clickCallback);
New code:
.. js ::
myForm.watch("valid", function(name, oldVal, newVal){
console.log(myForm.id + ": " + name + " changed from " +
oldVal + " to " + newVal);
});
myButton.on("click", clickCallback);
Templated Widgets
-----------------
The ``dijit/_Templated`` mixin has been split into ``dijit/_TemplatedMixin`` and ``dijit/_WidgetsInTemplateMixin``. In
addition, ``dojoAttachPoint`` and ``dojoAttachEvent`` have been changed to the HTML5 valid ``data-dojo-attach-point``
and ``data-dojo-attach-event``.
For example, old code like:
.. js ::
dojo.require("dojo.declare");
dojo.require("dijit._Widget");
dojo.require("dijit._Templated");
dojo.declare("SimpleTemplate", [dijit._Widget, dijit._Templated], {
templateString: ""
});
will change to:
.. js ::
require(["dojo/declare", "dijit/_WidgetBase", "dijit/_TemplatedMixin"],
function(declare, _WidgetBase, _TemplatedMixin){
declare("SimpleTemplate", [_WidgetBase, _TemplatedMixin], {
templateString: ""
});
});
If the above example had widgets in the templates, it would also mixin ``dijit/_WidgetsInTemplateMixin``.
To specify a template from a file, ``templatePath`` is no longer supported, and ``dojo.cache()`` shouldn't be used
either.
Old code:
.. js ::
templatePath: dojo.moduleUrl("templates", "myTemplate.html")
New code:
.. js ::
define([..., "dojo/text!./templates/myTemplate.html",
function(..., myTemplate){
...
templateString: myTemplate
The other change to widgets is that the ``waiRole`` and ``waiState`` parameters are no longer supported, since it's
now easy to use role and state directly.
For instance. Replace:
.. html ::
With:
.. html ::
custom setters
--------------
In 1.8 customer setters for attributes have names like _setXxxAttr(). In 2.0 the name will be changed to _xxxSetter().
attributeMap
------------
attributeMap in 1.x was a hash mapping widget attributes to DOM nodes. For example:
.. js ::
attributeMap: {
"index": "focusNode",
"style": "domNode"
}
Currently, this is achieved by making separate ``_xxxSetter`` attribute for each attribute to map. Originally
``_xxxSetter`` was a function to set a widget attribute. It can still be a function, but now it can also be an
object like one of the values from ``attributeMap``. (NB: In 1.8, it's _setXxxAttr() not _xxxSetter(). This will
change for 2.0.)
The code above would be expressed as:
.. js ::
_tabIndexSetter: "focusNode",
_styleSetter: "domNode"
this.connect(), this.subscribe(), this._supportingWidgets
---------------------------------------------------------
The ways to make a widget listen to DOMNode events, do advice on a regular function, subscribe to topics, and
to register a supporting widget have changed.
The new interface is to use the standard dojo methods dojo/on, dojo/aspect, dojo/topic, etc., and call this.own() to
register the handle to be released when the widget is destroyed. this.own() can be called multiple times, each with
one or more handles specified:
.. js ::
this.own(
// setup an event handler (automatically remove() when I'm destroyed)
on(this.domNode, "click", function(){ ... }),
// watch external object (automatically unwatch() when I'm destroyed)
aStatefulObject.watch("x", function(name, oVal, nVal){ ... }),
// create a supporting (internal) widget, to be destroyed when I'm destroyed
new MySupportingWidget(...)
);
Base Functionality
------------------
The methods previously loaded into ``dijit`` by default now must be explicitly loaded from various modules.
TODO: list stuff in ``dijit/registry``, ``dijit/a11y``.
dijit/focus, dijit/place, and dijit/popup
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The focus, place, and popup modules in ``dijit/_base`` have been promoted to root of ``dijit``, so they need included
explicitly by applications that don't want to include all of ``dijit/_base``.
There are a few API changes in the top level modules compared to the ones in ``dijit/_base`` (although for backwards
compatibility the modules in ``dijit/_base`` maintain their old API):
* ``Popup.around()`` (analogous to ``dijit.popup.placeAroundElement()``) takes a position parameter like
``["before", "after"]`` rather than a set of tuples like ``{BL: "TL", ...}``. In other words, ``Popup.around()``
replaces ``dijit.popup.placeAroundElement()`` but instead of ``dijit.getPopupAroundAlignment(xyz)``, just pass in
``xzy`` directly.
* ``dijit/focus`` doesn't include the selection related code, just focus related code
* ``dijit/focus`` provides ``.watch()`` and ``.on()`` methods to monitor the focused node and active widgets, rather
than publishing topics ``focusNode``, ``widgetBlur``, and ``widgetFocus``.
* Some methods in ``dijit/_base/popup`` used to take DOMNodes or widgets as a parameter; now they just take a widget
Also note that the new dijit/popup module is only available through the new AMD API, e.g.:
.. js ::
require(["dijit/popup"], function(popup){ popup.open(...); });
Some functions from ``dijit`` have been moved to ``dojo`` core.
* ``dojo/uacss`` will add classes to the ```` node like ``dj_ie``, representing the browser, browser version,
box model, etc. Formerly ``dojo.require("dijit.sniff")``.
* ``getBox()`` from ``dojo/window`` gets the viewport size. Formerly ``dijit.getViewport()``.
* ``get()`` from ``dojo/window`` converts a document to the corresponding window. Formerly
``dijit.getDocumentWindow()``
* ``scrollIntoView()`` from ``dojo/window`` scrolls a node into view, similar to ``node.scrollIntoView()`` but working
around browser quirks. Formerly ``dijit.scrollIntoView()``.
Editor
------
New way of registering plugins: TODO
SplitContainer, LayoutContainer
-------------------------------
Use BorderContainer instead. (TODO: examples)
Miscellaneous changes
---------------------
_Widget --> _WidgetBase (TODO: will probably rename again, to Widget)
DojoX
=====
The ``dojox`` namespace will be removed in Dojo 2.0. Some of the mature sub-packages will like migrate into Dojo Core
or into Dijit. The remaining code will be "spun off" into separate packages that will be available via package
management tools and a repository of packages.
In order to ensure your code can be easily migrated, refactoring it to fully leverage AMD and not relay upon the
``dojox`` global variable is critically important.