[ADD] doc for backbone-ish APIs
authorXavier Morel <xmo@openerp.com>
Mon, 11 Jun 2012 15:07:02 +0000 (17:07 +0200)
committerXavier Morel <xmo@openerp.com>
Mon, 11 Jun 2012 15:07:02 +0000 (17:07 +0200)
bzr revid: xmo@openerp.com-20120611150702-r4c9kyxmu8m7g4c0

doc/addons.rst
doc/index.rst
doc/widget.rst [new file with mode: 0644]

index da1f1f2..35878b0 100644 (file)
@@ -113,103 +113,6 @@ initializing the addon.
 Creating new standard roles
 ---------------------------
 
-Widget
-++++++
-
-This is the base class for all visual components. It provides a number of
-services for the management of a DOM subtree:
-
-* 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.base.Widget.appendTo`
-    Renders the widget and inserts it as the last child of the target, uses
-    `.appendTo()`_
-
-  :js:func:`~openerp.base.Widget.prependTo`
-    Renders the widget and inserts it as the first child of the target, uses
-    `.prependTo()`_
-
-  :js:func:`~openerp.base.Widget.insertAfter`
-    Renders the widget and inserts it as the preceding sibling of the target,
-    uses `.insertAfter()`_
-
-  :js:func:`~openerp.base.Widget.insertBefore`
-    Renders the widget and inserts it as the following sibling of the target,
-    uses `.insertBefore()`_
-
-:js:class:`~openerp.base.Widget` inherits from
-:js:class:`~openerp.base.SessionAware`, so subclasses can easily access the
-RPC layers.
-
-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.stop();
-
-will unbind all DOM events, remove the widget's content from the DOM and
-destroy all widget data.
-
 Views
 +++++
 
@@ -541,18 +444,6 @@ Python
 .. _promise object:
     http://api.jquery.com/deferred.promise/
 
-.. _.appendTo():
-    http://api.jquery.com/appendTo/
-
-.. _.prependTo():
-    http://api.jquery.com/prependTo/
-
-.. _.insertAfter():
-    http://api.jquery.com/insertAfter/
-
-.. _.insertBefore():
-    http://api.jquery.com/insertBefore/
-
 .. _Rosetta:
 .. _Launchpad's own translation tool:
     https://help.launchpad.net/Translations
index 9f54b87..1f66042 100644 (file)
@@ -16,6 +16,7 @@ Contents:
     async
     rpc
 
+    widget
     search-view
 
 Older stuff
diff --git a/doc/widget.rst b/doc/widget.rst
new file mode 100644 (file)
index 0000000..9868e40
--- /dev/null
@@ -0,0 +1,270 @@
+User Interaction: 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.base.Widget.appendTo`
+    Renders the widget and inserts it as the last child of the target, uses
+    `.appendTo()`_
+
+  :js:func:`~openerp.base.Widget.prependTo`
+    Renders the widget and inserts it as the first child of the target, uses
+    `.prependTo()`_
+
+  :js:func:`~openerp.base.Widget.insertAfter`
+    Renders the widget and inserts it as the preceding sibling of the target,
+    uses `.insertAfter()`_
+
+  :js:func:`~openerp.base.Widget.insertBefore`
+    Renders the widget and inserts it as the following sibling of the target,
+    uses `.insertBefore()`_
+
+* Backbone-compatible shortcuts
+
+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.
+
+.. note::
+
+    both attributes are compatible with Backbone's equivalent, there
+    is also the :js:attr:`~openerp.web.Widget.$element` attribute
+    which aliases to :js:attr:`~openerp.web.Widget.$el` and remains
+    for backwards compatiblity reasons.
+
+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.
+
+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.$element.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.
+
+    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
+