[MOV] developer: move section on patching javascript

This commit is a move, but the content was also slightly
updated/reformatted.

closes odoo/documentation#1288

X-original-commit: f206233ebc
Signed-off-by: Géry Debongnie (ged) <ged@openerp.com>
This commit is contained in:
Géry Debongnie
2021-10-31 09:17:59 +00:00
parent aec3e2d9d9
commit efb327479f
4 changed files with 227 additions and 157 deletions
@@ -2246,160 +2246,3 @@ For more information, look into the `control_panel_renderer.js <https://github.c
.. _datepicker: https://github.com/Eonasdan/bootstrap-datetimepicker
Patching code
=============
Sometimes we need to modify an object or a class in place. To achieve that, Odoo
provides the utility function ``patch``. It is mostly useful to override/update
the behaviour of some other component/piece of code that one does not control.
Obviously, it is not the primary tool of working with Odoo. It should be used
with care.
Patching a simple object
------------------------
Here is a simple example of how an object can be patched:
.. code-block:: javascript
const { patch } = require("web.utils");
const object = {
field: "a field",
fn() {
// do something
},
};
patch(object, "patch name", {
fn() {
// do things
},
});
When patching functions, we usually want to be able to access the ``parent``
function. Since we are working with patch objects, not ES6 classes, we cannot
use the native ``super`` keyword. So, Odoo provides a special method to simulate
this behaviour: ``this._super``:
.. code-block:: javascript
patch(object, "_super patch", {
fn() {
this._super(...arguments);
// do other things
},
});
.. warning::
``this._super`` is reassigned after each patched function is called.
This means that if you use an asynchronous function in the patch then you
cannot call ``this._super`` after an ``await``, because it may or may not be
the function that you expect. The correct way to do that is to keep a reference
to the initial ``_super`` method:
.. code-block:: javascript
patch(object, "async _super patch", {
async myAsyncFn() {
const _super = this._super;
await Promise.resolve();
await _super(...arguments);
// await this._super(...arguments); // this._super is undefined.
},
});
Getters and setters are supported too!
.. code-block:: javascript
patch(object, "getter/setter patch", {
get number() {
return this._super() / 2;
},
set number(value) {
this._super(value * 2);
},
});
Native JS class
---------------
The ``patch`` function is similar to ``OdooClass.include``, but it is designed
to work with anything: object, Odoo class, ES6 class.
However, since ES6 classes work with the javascript prototypal inheritance, when
one wishes to patch a standard method from a class, then we actually need to patch
the ``prototype``:
.. code-block:: javascript
class MyClass {
static myStaticFn() {...}
myPrototypeFn() {...}
}
// this will patch static properties!!!
patch(MyClass, "static patch", {
myStaticFn() {...},
});
// this is probably the usual case: patching a class method
patch(MyClass.prototype, "prototype patch", {
myPrototypeFn() {...},
});
Also, Javascript handle the constructor in a special native way which makes it
impossible to be patched. The only workaround is to call a method in the original
constructor and patch that method instead:
.. code-block:: javascript
class MyClass {
constructor() {
this.setup();
}
setup() {
this.number = 1;
}
}
patch(MyClass.prototype, "constructor", {
setup() {
this._super(...arguments);
this.doubleNumber = this.number * 2;
},
});
Note that for this reason, Owl component have all the ``setup`` method builtin.
Therefore, patching a component constructor is always possible:
.. code-block:: javascript
patch(MyComponent.prototype, "my patch", {
setup() {
useMyHook();
},
});
Removing a patch
-----------------
In tests, it is sometimes useful to remove a patch for a specific test, for instance,
to remove a patch applied when another addon is installed, which overrides the behavior
implemented in the addon where the test is defined. The function ``unpatch`` exists for
that purpose. It returns the removed patch, such that it can be re-applied at the end
of the test.
.. code-block:: javascript
const p = unpatch(object, "patch name");
// test stuff here
patch(object, "patch name", p);