+++ /dev/null
-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
-
+.. highlight:: javascript
+
+.. default-domain:: js
+
==========
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
===
Web Client
==========
+
+.. [#eventsdelegation] not all DOM events are compatible with events delegation
+