275 lines
9.1 KiB
ReStructuredText
275 lines
9.1 KiB
ReStructuredText
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.$element` 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.r
|
|
|
|
.. 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.$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
|
|
|