.. _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.