[ADD] JS widgets reference documentation
authorXavier Morel <xmo@openerp.com>
Fri, 12 Sep 2014 12:54:27 +0000 (14:54 +0200)
committerXavier Morel <xmo@openerp.com>
Tue, 7 Oct 2014 08:21:44 +0000 (10:21 +0200)
Also fixed Widget#alive's behavior and tested it

addons/web/doc/index.rst
addons/web/doc/widget.rst [deleted file]
addons/web/static/src/js/openerpframework.js
addons/web/static/test/framework.js
doc/_themes/odoodoc/static/style.css
doc/_themes/odoodoc/static/style.less
doc/reference/javascript.rst

index 511a271..bad653e 100644 (file)
@@ -34,7 +34,6 @@ Javascript
     :maxdepth: 1
 
     guidelines
-    widget
     rpc
     async
     client_action
diff --git a/addons/web/doc/widget.rst b/addons/web/doc/widget.rst
deleted file mode 100644 (file)
index 7c4a237..0000000
+++ /dev/null
@@ -1,287 +0,0 @@
-Widget
-======
-
-.. js:class:: openerp.web.Widget
-
-This is the base class for all visual components. It corresponds to an MVC
-view. It provides a number of services to handle a section of a page:
-
-* Rendering with QWeb
-
-* Parenting-child relations
-
-* Life-cycle management (including facilitating children destruction when a
-  parent object is removed)
-
-* DOM insertion, via jQuery-powered insertion methods. Insertion targets can
-  be anything the corresponding jQuery method accepts (generally selectors,
-  DOM nodes and jQuery objects):
-
-  :js:func:`~openerp.web.Widget.appendTo`
-    Renders the widget and inserts it as the last child of the target, uses
-    `.appendTo()`_
-
-  :js:func:`~openerp.web.Widget.prependTo`
-    Renders the widget and inserts it as the first child of the target, uses
-    `.prependTo()`_
-
-  :js:func:`~openerp.web.Widget.insertAfter`
-    Renders the widget and inserts it as the preceding sibling of the target,
-    uses `.insertAfter()`_
-
-  :js:func:`~openerp.web.Widget.insertBefore`
-    Renders the widget and inserts it as the following sibling of the target,
-    uses `.insertBefore()`_
-
-* Backbone-compatible shortcuts
-
-.. _widget-dom_root:
-
-DOM Root
---------
-
-A :js:class:`~openerp.web.Widget` is responsible for a section of the
-page materialized by the DOM root of the widget. The DOM root is
-available via the :js:attr:`~openerp.web.Widget.el` and
-:js:attr:`~openerp.web.Widget.$el` attributes, which are
-respectively the raw DOM Element and the jQuery wrapper around the DOM
-element.
-
-There are two main ways to define and generate this DOM root:
-
-.. js:attribute:: openerp.web.Widget.template
-
-    Should be set to the name of a QWeb template (a
-    :js:class:`String`). If set, the template will be rendered after
-    the widget has been initialized but before it has been
-    started. The root element generated by the template will be set as
-    the DOM root of the widget.
-
-.. js:attribute:: openerp.web.Widget.tagName
-
-    Used if the widget has no template defined. Defaults to ``div``,
-    will be used as the tag name to create the DOM element to set as
-    the widget's DOM root. It is possible to further customize this
-    generated DOM root with the following attributes:
-
-    .. js:attribute:: openerp.web.Widget.id
-
-        Used to generate an ``id`` attribute on the generated DOM
-        root.
-
-    .. js:attribute:: openerp.web.Widget.className
-
-        Used to generate a ``class`` attribute on the generated DOM root.
-
-    .. js:attribute:: openerp.web.Widget.attributes
-
-        Mapping (object literal) of attribute names to attribute
-        values. Each of these k:v pairs will be set as a DOM attribute
-        on the generated DOM root.
-
-    None of these is used in case a template is specified on the widget.
-
-The DOM root can also be defined programmatically by overridding
-
-.. js:function:: openerp.web.Widget.renderElement
-
-    Renders the widget's DOM root and sets it. The default
-    implementation will render a set template or generate an element
-    as described above, and will call
-    :js:func:`~openerp.web.Widget.setElement` on the result.
-
-    Any override to :js:func:`~openerp.web.Widget.renderElement` which
-    does not call its ``_super`` **must** call
-    :js:func:`~openerp.web.Widget.setElement` with whatever it
-    generated or the widget's behavior is undefined.
-
-    .. note::
-
-        The default :js:func:`~openerp.web.Widget.renderElement` can
-        be called repeatedly, it will *replace* the previous DOM root
-        (using ``replaceWith``). However, this requires that the
-        widget correctly sets and unsets its events (and children
-        widgets). Generally,
-        :js:func:`~openerp.web.Widget.renderElement` should not be
-        called repeatedly unless the widget advertizes this feature.
-
-Accessing DOM content
-~~~~~~~~~~~~~~~~~~~~~
-
-Because a widget is only responsible for the content below its DOM
-root, there is a shortcut for selecting sub-sections of a widget's
-DOM:
-
-.. js:function:: openerp.web.Widget.$(selector)
-
-    Applies the CSS selector specified as parameter to the widget's
-    DOM root.
-
-    .. code-block:: javascript
-
-        this.$(selector);
-
-    is functionally identical to:
-
-    .. code-block:: javascript
-
-        this.$el.find(selector);
-
-    :param String selector: CSS selector
-    :returns: jQuery object
-
-    .. note:: this helper method is compatible with
-              ``Backbone.View.$``
-
-Resetting the DOM root
-~~~~~~~~~~~~~~~~~~~~~~
-
-.. js:function:: openerp.web.Widget.setElement(element)
-
-    Re-sets the widget's DOM root to the provided element, also
-    handles re-setting the various aliases of the DOM root as well as
-    unsetting and re-setting delegated events.
-
-    :param Element element: a DOM element or jQuery object to set as
-                            the widget's DOM root
-
-    .. note:: should be mostly compatible with `Backbone's
-              setElement`_
-
-DOM events handling
--------------------
-
-A widget will generally need to respond to user action within its
-section of the page. This entails binding events to DOM elements.
-
-To this end, :js:class:`~openerp.web.Widget` provides an shortcut:
-
-.. js:attribute:: openerp.web.Widget.events
-
-    Events are a mapping of ``event selector`` (an event name and a
-    CSS selector separated by a space) to a callback. The callback can
-    be either a method name in the widget or a function. In either
-    case, the ``this`` will be set to the widget:
-
-    .. code-block:: javascript
-
-        events: {
-            'click p.oe_some_class a': 'some_method',
-            'change input': function (e) {
-                e.stopPropagation();
-            }
-        },
-
-    The selector is used for jQuery's `event delegation`_, the
-    callback will only be triggered for descendants of the DOM root
-    matching the selector [0]_. If the selector is left out (only an
-    event name is specified), the event will be set directly on the
-    widget's DOM root.
-
-.. js:function:: openerp.web.Widget.delegateEvents
-
-    This method is in charge of binding
-    :js:attr:`~openerp.web.Widget.events` to the DOM. It is
-    automatically called after setting the widget's DOM root.
-
-    It can be overridden to set up more complex events than the
-    :js:attr:`~openerp.web.Widget.events` map allows, but the parent
-    should always be called (or :js:attr:`~openerp.web.Widget.events`
-    won't be handled correctly).
-
-.. js:function:: openerp.web.Widget.undelegateEvents
-
-    This method is in charge of unbinding
-    :js:attr:`~openerp.web.Widget.events` from the DOM root when the
-    widget is destroyed or the DOM root is reset, in order to avoid
-    leaving "phantom" events.
-
-    It should be overridden to un-set any event set in an override of
-    :js:func:`~openerp.web.Widget.delegateEvents`.
-
-.. note:: this behavior should be compatible with `Backbone's
-          delegateEvents`_, apart from not accepting any argument.
-
-Subclassing Widget
-------------------
-
-:js:class:`~openerp.base.Widget` is subclassed in the standard manner (via the
-:js:func:`~openerp.base.Class.extend` method), and provides a number of
-abstract properties and concrete methods (which you may or may not want to
-override). Creating a subclass looks like this:
-
-.. code-block:: javascript
-
-    var MyWidget = openerp.base.Widget.extend({
-        // QWeb template to use when rendering the object
-        template: "MyQWebTemplate",
-
-        init: function(parent) {
-            this._super(parent);
-            // insert code to execute before rendering, for object
-            // initialization
-        },
-        start: function() {
-            this._super();
-            // post-rendering initialization code, at this point
-            // ``this.$element`` has been initialized
-            this.$element.find(".my_button").click(/* an example of event binding * /);
-
-            // if ``start`` is asynchronous, return a promise object so callers
-            // know when the object is done initializing
-            return this.rpc(/* … */)
-        }
-    });
-
-The new class can then be used in the following manner:
-
-.. code-block:: javascript
-
-    // Create the instance
-    var my_widget = new MyWidget(this);
-    // Render and insert into DOM
-    my_widget.appendTo(".some-div");
-
-After these two lines have executed (and any promise returned by ``appendTo``
-has been resolved if needed), the widget is ready to be used.
-
-.. note:: the insertion methods will start the widget themselves, and will
-          return the result of :js:func:`~openerp.base.Widget.start()`.
-
-          If for some reason you do not want to call these methods, you will
-          have to first call :js:func:`~openerp.base.Widget.render()` on the
-          widget, then insert it into your DOM and start it.
-
-If the widget is not needed anymore (because it's transient), simply terminate
-it:
-
-.. code-block:: javascript
-
-    my_widget.destroy();
-
-will unbind all DOM events, remove the widget's content from the DOM and
-destroy all widget data.
-
-.. [0] not all DOM events are compatible with events delegation
-
-.. _.appendTo():
-    http://api.jquery.com/appendTo/
-
-.. _.prependTo():
-    http://api.jquery.com/prependTo/
-
-.. _.insertAfter():
-    http://api.jquery.com/insertAfter/
-
-.. _.insertBefore():
-    http://api.jquery.com/insertBefore/
-
-.. _event delegation:
-    http://api.jquery.com/delegate/
-
-.. _Backbone's setElement:
-    http://backbonejs.org/#View-setElement
-
-.. _Backbone's delegateEvents:
-    http://backbonejs.org/#View-delegateEvents
-
index f190fef..de8f5f6 100644 (file)
@@ -258,24 +258,24 @@ openerp.ParentedMixin = {
                               current object is destroyed.
     */
     alive: function(promise, reject) {
-        var def = $.Deferred();
         var self = this;
-        promise.done(function() {
-            if (! self.isDestroyed()) {
-                if (! reject)
+        return $.Deferred(function (def) {
+            promise.then(function () {
+                if (!self.isDestroyed()) {
                     def.resolve.apply(def, arguments);
-                else
-                    def.reject();
-            }
-        }).fail(function() {
-            if (! self.isDestroyed()) {
-                if (! reject)
+                }
+            }, function () {
+                if (!self.isDestroyed()) {
                     def.reject.apply(def, arguments);
-                else
+                }
+            }).always(function () {
+                if (reject) {
+                    // noop if def already resolved or rejected
                     def.reject();
-            }
-        });
-        return def.promise();
+                }
+                // otherwise leave promise in limbo
+            });
+        }).promise();
     },
     /**
      * Inform the object it should destroy itself, releasing any
index 237bdc1..3fff6b7 100644 (file)
@@ -400,8 +400,68 @@ ropenerp.testing.section('Widget.events', {
         ok(newclicked, "undelegate should only unbind events it created");
     });
 });
+ropenerp.testing.section('Widget.async', {
 
-ropenerp.testing.section('server-formats', {
+}, function (test) {
+    test("alive(alive)", {asserts: 1}, function () {
+        var w = new (openerp.Widget.extend({}));
+        return $.async_when(w.start())
+        .then(function () { return w.alive($.async_when()) })
+        .then(function () { ok(true); });
+    });
+    test("alive(dead)", {asserts: 1}, function () {
+        var w = new (openerp.Widget.extend({}));
+
+        return $.Deferred(function (d) {
+            $.async_when(w.start())
+            .then(function () {
+                // destroy widget
+                w.destroy();
+                var promise = $.async_when();
+                // leave time for alive() to do its stuff
+                promise.then(function () {
+                    return $.async_when();
+                }).then(function () {
+                    ok(true);
+                    d.resolve();
+                });
+                // ensure that w.alive() refuses to resolve or reject
+                return w.alive(promise);
+            }).always(function () {
+                d.reject();
+                ok(false, "alive() should not terminate by default");
+            })
+        });
+    });
+
+
+    test("alive(alive, true)", {asserts: 1}, function () {
+        var w = new (openerp.Widget.extend({}));
+        return $.async_when(w.start())
+        .then(function () { return w.alive($.async_when(), true) })
+        .then(function () { ok(true); });
+    });
+    test("alive(dead, true)", {asserts: 1, fail_on_rejection: false}, function () {
+        var w = new (openerp.Widget.extend({}));
+
+        return $.async_when(w.start())
+        .then(function () {
+            // destroy widget
+            w.destroy();
+            console.log('destroyed');
+            return w.alive($.async_when().done(function () { console.log('when'); }), true);
+        }).then(function () {
+            console.log('unfailed')
+            ok(false, "alive(p, true) should fail its promise");
+        }, function () {
+            console.log('failed')
+            ok(true, "alive(p, true) should fail its promise");
+        });
+    });
+
+});
+
+    ropenerp.testing.section('server-formats', {
     dependencies: ['web.core', 'web.dates']
 }, function (test) {
     test('Parse server datetime', function () {
index 9fb81c9..9b5468f 100644 (file)
@@ -6714,3 +6714,6 @@ pre {
   word-break: normal;
   word-wrap: normal;
 }
+.descclassname {
+  opacity: 0.5;
+}
index e5c0030..ca8be90 100644 (file)
@@ -551,3 +551,8 @@ pre {
   word-break: normal;
   word-wrap: normal;
 }
+
+// lighten js namespace/class name
+.descclassname {
+  opacity: 0.5;
+}
index 07f38b7..18491ea 100644 (file)
@@ -1,3 +1,7 @@
+.. highlight:: javascript
+
+.. default-domain:: js
+
 ==========
 Javascript
 ==========
@@ -5,8 +9,400 @@ Javascript
 Widgets
 =======
 
-.. qweb integration: ``template`` is an (optional) automatically rendered
-   template
+.. class:: openerp.Widget
+
+This is the base class for all visual components. It corresponds to an MVC
+view. It provides a number of services to handle a section of a page:
+
+* Rendering with QWeb
+
+* Parenting-child relations
+
+* Life-cycle management (including facilitating children destruction when a
+  parent object is removed)
+
+* DOM insertion, via jQuery-powered insertion methods. Insertion targets can
+  be anything the corresponding jQuery method accepts (generally selectors,
+  DOM nodes and jQuery objects):
+
+  :func:`~openerp.Widget.appendTo`
+    Renders the widget and inserts it as the last child of the target, uses
+    `.appendTo()`_
+
+  :func:`~openerp.Widget.prependTo`
+    Renders the widget and inserts it as the first child of the target, uses
+    `.prependTo()`_
+
+  :func:`~openerp.Widget.insertAfter`
+    Renders the widget and inserts it as the preceding sibling of the target,
+    uses `.insertAfter()`_
+
+  :func:`~openerp.Widget.insertBefore`
+    Renders the widget and inserts it as the following sibling of the target,
+    uses `.insertBefore()`_
+
+* Backbone-compatible shortcuts
+
+.. _widget-dom_root:
+
+DOM Root
+--------
+
+A :class:`~openerp.Widget` is responsible for a section of the
+page materialized by the DOM root of the widget.
+
+A widget's DOM root is available via two attributes:
+
+.. attribute:: openerp.Widget.el
+
+    raw DOM element set as root to the widget
+
+.. attribute:: openerp.Widget.$el
+
+    jQuery wrapper around :attr:`~openerp.Widget.el`
+
+There are two main ways to define and generate this DOM root:
+
+.. attribute:: openerp.Widget.template
+
+    Should be set to the name of a :ref:`QWeb template <reference/qweb>`.
+    If set, the template will be rendered after the widget has been
+    initialized but before it has been started. The root element generated by
+    the template will be set as the DOM root of the widget.
+
+.. attribute:: openerp.Widget.tagName
+
+    Used if the widget has no template defined. Defaults to ``div``,
+    will be used as the tag name to create the DOM element to set as
+    the widget's DOM root. It is possible to further customize this
+    generated DOM root with the following attributes:
+
+    .. attribute:: openerp.Widget.id
+
+        Used to generate an ``id`` attribute on the generated DOM
+        root.
+
+    .. attribute:: openerp.Widget.className
+
+        Used to generate a ``class`` attribute on the generated DOM root.
+
+    .. attribute:: openerp.Widget.attributes
+
+        Mapping (object literal) of attribute names to attribute
+        values. Each of these k:v pairs will be set as a DOM attribute
+        on the generated DOM root.
+
+    None of these is used in case a template is specified on the widget.
+
+The DOM root can also be defined programmatically by overridding
+
+.. function:: openerp.Widget.renderElement
+
+    Renders the widget's DOM root and sets it. The default
+    implementation will render a set template or generate an element
+    as described above, and will call
+    :func:`~openerp.Widget.setElement` on the result.
+
+    Any override to :func:`~openerp.Widget.renderElement` which
+    does not call its ``_super`` **must** call
+    :func:`~openerp.Widget.setElement` with whatever it
+    generated or the widget's behavior is undefined.
+
+    .. note::
+
+        The default :func:`~openerp.Widget.renderElement` can
+        be called repeatedly, it will *replace* the previous DOM root
+        (using ``replaceWith``). However, this requires that the
+        widget correctly sets and unsets its events (and children
+        widgets). Generally, :func:`~openerp.Widget.renderElement` should
+        not be called repeatedly unless the widget advertizes this feature.
+
+Using a widget
+''''''''''''''
+
+A widget's lifecycle has 3 main phases:
+
+* creation and initialization of the widget instance
+
+  .. function:: openerp.Widget.init(parent)
+
+       initialization method of widgets, synchronous, can be overridden to
+       take more parameters from the widget's creator/parent
+
+       :param parent: the current widget's parent, used to handle automatic
+                      destruction and even propagation. Can be ``null`` for
+                      the widget to have no parent.
+       :type parent: :class:`~openerp.Widget`
+
+* DOM injection and startup, this is done by calling one of:
+
+  .. function:: openerp.Widget.appendTo(element)
+
+    Renders the widget and inserts it as the last child of the target, uses
+    `.appendTo()`_
+
+  .. function:: openerp.Widget.prependTo(element)
+
+    Renders the widget and inserts it as the first child of the target, uses
+    `.prependTo()`_
+
+  .. function:: openerp.Widget.insertAfter(element)
+
+    Renders the widget and inserts it as the preceding sibling of the target,
+    uses `.insertAfter()`_
+
+  .. function:: openerp.Widget.insertBefore(element)
+
+    Renders the widget and inserts it as the following sibling of the target,
+    uses `.insertBefore()`_
+
+  All of these methods accept whatever the corresponding jQuery method accepts
+  (CSS selectors, DOM nodes or jQuery objects). They all return a promise and
+  are charged with three tasks:
+
+  * render the widget's root element via
+    :func:`~openerp.Widget.renderElement`
+  * insert the widget's root element in the DOM using whichever jQuery method
+    they match
+  * start the widget, and return the result of starting it
+
+    .. function:: openerp.Widget.start()
+
+        asynchronous startup of the widget once it's been injected in the DOM,
+        generally used to perform asynchronous RPC calls to fetch whatever
+        remote data is necessary for the widget to do its work.
+
+        Must return a deferred_ to indicate when its work is done.
+
+        A widget is *not guaranteed* to work correctly until its
+        :func:`~openerp.Widget.start` method has finished executing. The
+        widget's parent/creator must wait for a widget to be fully started
+        before interacting with it
+
+        :returns: deferred_ object
+
+* widget destruction and cleanup
+
+  .. function:: openerp.Widget.destroy()
+
+    destroys the widget's children, unbinds its events and removes its root
+    from the DOM. Automatically called when the widget's parent is destroyed,
+    must be called explicitly if the widget has no parents or if it is
+    removed but its parent remains.
+
+    A widget being destroyed is automatically unlinked from its parent.
+
+Because a widget can be destroyed at any time, widgets also have utility
+methods to handle this case:
+
+.. function:: openerp.Widget.alive(deferred[, reject=false])
+
+    A significant issue with RPC and destruction is that an RPC call may take
+    a long time to execute and return while a widget is being destroyed or
+    after it has been destroyed, trying to execute its operations on a widget
+    in a broken/invalid state.
+
+    This is a frequent source of errors or strange behaviors.
+
+    :func:`~openerp.Widget.alive` can be used to wrap an RPC call,
+    ensuring that whatever operations should be executed when the call ends
+    are only executed if the widget is still alive::
+
+        this.alive(this.model.query().all()).then(function (records) {
+            // would break if executed after the widget is destroyed, wrapping
+            // rpc in alive() prevents execution
+            _.each(records, function (record) {
+                self.$el.append(self.format(record));
+            });
+        });
+
+    :param deferred: a deferred_ object to wrap
+    :param reject: by default, if the RPC call returns after the widget has
+                   been destroyed the returned deferred_ is left in limbo
+                   (neither resolved nor rejected). If ``reject`` is set to
+                   ``true``, the deferred_ will be rejected instead.
+    :returns: deferred_ object
+
+.. function:: openerp.Widget.isDestroyed()
+
+    :returns: ``true`` if the widget is being or has been destroyed, ``false``
+              otherwise
+
+Accessing DOM content
+'''''''''''''''''''''
+
+Because a widget is only responsible for the content below its DOM
+root, there is a shortcut for selecting sub-sections of a widget's
+DOM:
+
+.. function:: openerp.Widget.$(selector)
+
+    Applies the CSS selector specified as parameter to the widget's
+    DOM root.
+
+    ::
+
+        this.$(selector);
+
+    is functionally identical to::
+
+        this.$el.find(selector);
+
+    :param String selector: CSS selector
+    :returns: jQuery object
+
+    .. note:: this helper method is compatible with
+              ``Backbone.View.$``
+
+Resetting the DOM root
+''''''''''''''''''''''
+
+.. function:: openerp.Widget.setElement(element)
+
+    Re-sets the widget's DOM root to the provided element, also
+    handles re-setting the various aliases of the DOM root as well as
+    unsetting and re-setting delegated events.
+
+    :param Element element: a DOM element or jQuery object to set as
+                            the widget's DOM root
+
+    .. note:: should be mostly compatible with `Backbone's
+              setElement`_
+
+DOM events handling
+-------------------
+
+A widget will generally need to respond to user action within its
+section of the page. This entails binding events to DOM elements.
+
+To this end, :class:`~openerp.Widget` provides an shortcut:
+
+.. attribute:: openerp.Widget.events
+
+    Events are a mapping of ``event selector`` (an event name and a
+    CSS selector separated by a space) to a callback. The callback can
+    be the name of a widget's method or a function object. In either case, the
+    ``this`` will be set to the widget::
+
+        events: {
+            'click p.oe_some_class a': 'some_method',
+            'change input': function (e) {
+                e.stopPropagation();
+            }
+        },
+
+    The selector is used for jQuery's `event delegation`_, the
+    callback will only be triggered for descendants of the DOM root
+    matching the selector\ [#eventsdelegation]_. If the selector is left out
+    (only an event name is specified), the event will be set directly on the
+    widget's DOM root.
+
+.. function:: openerp.Widget.delegateEvents
+
+    This method is in charge of binding
+    :attr:`~openerp.Widget.events` to the DOM. It is
+    automatically called after setting the widget's DOM root.
+
+    It can be overridden to set up more complex events than the
+    :attr:`~openerp.Widget.events` map allows, but the parent
+    should always be called (or :attr:`~openerp.Widget.events`
+    won't be handled correctly).
+
+.. function:: openerp.Widget.undelegateEvents
+
+    This method is in charge of unbinding
+    :attr:`~openerp.Widget.events` from the DOM root when the
+    widget is destroyed or the DOM root is reset, in order to avoid
+    leaving "phantom" events.
+
+    It should be overridden to un-set any event set in an override of
+    :func:`~openerp.Widget.delegateEvents`.
+
+.. note:: this behavior should be compatible with `Backbone's
+          delegateEvents`_, apart from not accepting any argument.
+
+Subclassing Widget
+------------------
+
+:class:`~openerp.Widget` is subclassed in the standard manner (via the
+:func:`~openerp.Class.extend` method), and provides a number of
+abstract properties and concrete methods (which you may or may not want to
+override). Creating a subclass looks like this::
+
+    var MyWidget = openerp.Widget.extend({
+        // QWeb template to use when rendering the object
+        template: "MyQWebTemplate",
+        events: {
+            // events binding example
+            'click .my-button': 'handle_click',
+        },
+
+        init: function(parent) {
+            this._super(parent);
+            // insert code to execute before rendering, for object
+            // initialization
+        },
+        start: function() {
+            var sup = this._super();
+            // post-rendering initialization code, at this point
+
+            // allows multiplexing deferred objects
+            return $.when(
+                // propagate asynchronous signal from parent class
+                sup,
+                // return own's asynchronous signal
+                this.rpc(/* … */))
+        }
+    });
+
+The new class can then be used in the following manner::
+
+    // Create the instance
+    var my_widget = new MyWidget(this);
+    // Render and insert into DOM
+    my_widget.appendTo(".some-div");
+
+After these two lines have executed (and any promise returned by
+:func:`~openerp.Widget.appendTo` has been resolved if needed), the widget is
+ready to be used.
+
+.. note:: the insertion methods will start the widget themselves, and will
+          return the result of :func:`~openerp.Widget.start()`.
+
+          If for some reason you do not want to call these methods, you will
+          have to first call :func:`~openerp.Widget.render()` on the
+          widget, then insert it into your DOM and start it.
+
+If the widget is not needed anymore (because it's transient), simply terminate
+it::
+
+    my_widget.destroy();
+
+will unbind all DOM events, remove the widget's content from the DOM and
+destroy all widget data.
+
+.. _.appendTo():
+    http://api.jquery.com/appendTo/
+
+.. _.prependTo():
+    http://api.jquery.com/prependTo/
+
+.. _.insertAfter():
+    http://api.jquery.com/insertAfter/
+
+.. _.insertBefore():
+    http://api.jquery.com/insertBefore/
+
+.. _event delegation:
+    http://api.jquery.com/delegate/
+
+.. _Backbone's setElement:
+    http://backbonejs.org/#View-setElement
+
+.. _Backbone's delegateEvents:
+    http://backbonejs.org/#View-delegateEvents
+
+.. _deferred: http://api.jquery.com/category/deferred-object/
 
 RPC
 ===
@@ -15,3 +411,6 @@ RPC
 
 Web Client
 ==========
+
+.. [#eventsdelegation] not all DOM events are compatible with events delegation
+