diff options
| author | Lucas Perais (lpe) <lpe@odoo.com> | 2020-01-22 12:38:10 +0000 |
|---|---|---|
| committer | aab-odoo <aab@odoo.com> | 2020-02-03 09:15:02 +0000 |
| commit | 6bdcf054fdeef1b02cbb20bdb00fcba648725f36 (patch) | |
| tree | 1e61b50fd1fdd57662139b4ec00931f0c0afc8b2 /addons/web/static/src/js/core | |
| parent | a3e24efc19e68dc838f645413cd392a64a9e3d8b (diff) | |
[IMP] web: custom_hooks: dynamic inheritable event handling
This commit's intention is to bring class inheritance of event handlers
to components.
Use case:
A component class may want to set generic event handlers for all classes
inheriting from it, in order to set shared event behavior from the start
It is the case in many parts of Odoo, in particular in the cases of fields
which need at least to react on some keyboard event for navigation
The way to implement such a behavior is to make use of the
`useListener` OWL hook, by passing as arguments
event name and the handler function
Diffstat (limited to 'addons/web/static/src/js/core')
| -rw-r--r-- | addons/web/static/src/js/core/custom_hooks.js | 70 |
1 files changed, 70 insertions, 0 deletions
diff --git a/addons/web/static/src/js/core/custom_hooks.js b/addons/web/static/src/js/core/custom_hooks.js index 81f625349b7..a99576b73ce 100644 --- a/addons/web/static/src/js/core/custom_hooks.js +++ b/addons/web/static/src/js/core/custom_hooks.js @@ -37,7 +37,77 @@ odoo.define('web.custom_hooks', function () { }; } + /** + * The useListener hook offers an alternative to Owl's classical event + * registration mechanism (with attribute 't-on-eventName' in xml). It is + * especially useful for abstract components, meant to be extended by + * specific ones. If those abstract components need to define event handlers, + * but don't have any template (because the template completely depends on + * specific cases), then using the 't-on' mechanism isn't adequate, as the + * handlers would be lost by the template override. In this case, using this + * hook instead is more convenient. + * + * Example: navigation event handling in AbstractField + * + * Usage: like all Owl hooks, this function has to be called in the + * constructor of an Owl component: + * + * useListener('click', () => { console.log('clicked'); }); + * + * An optional native query selector can be specified as second argument for + * event delegation. In this case, the handler is only called if the event + * is triggered on an element matching the given selector. + * + * useListener('click', 'button', () => { console.log('clicked'); }); + * + * Note: components that alter the event's target (e.g. Portal) are not + * expected to behave as expected with event delegation. + * + * @param {string} eventName the name of the event + * @param {string} [querySelector] a JS native selector for event delegation + * @param {function} handler the event handler (will be bound to the component) + */ + function useListener(eventName, querySelector, handler) { + if (arguments.length === 2) { + querySelector = null; + handler = arguments[1]; + } + if (!(typeof handler === 'function')) { + throw new Error('The handler must be a function'); + } + + const comp = Component.current; + let boundHandler; + if (querySelector) { + boundHandler = function (ev) { + let el = ev.target; + let target; + while (el && !target) { + if (el.matches(querySelector)) { + target = el; + } else if (el === comp.el) { + el = null; + } else { + el = el.parentElement; + } + } + if (el) { + handler.call(comp, ev); + } + }; + } else { + boundHandler = handler.bind(comp); + } + onMounted(function () { + comp.el.addEventListener(eventName, boundHandler); + }); + onWillUnmount(function () { + comp.el.removeEventListener(eventName, boundHandler); + }); + } + return { useFocusOnUpdate, + useListener, }; }); |
