[IMP] web: update documentation of patch function
This commit updates the documentation of the patch function with the changes done in https://github.com/odoo/odoo/pull/125716. closes odoo/documentation#5017 Signed-off-by: Antoine Vandevenne (anv) <anv@odoo.com>
This commit is contained in:
parent
dad4a47c9a
commit
775e80ed92
@ -18,24 +18,18 @@ Description
|
||||
|
||||
The patch function is located in `@web/core/utils/patch`:
|
||||
|
||||
.. js:function:: patch(obj, patchName, patchValue, options)
|
||||
.. js:function:: patch(objToPatch, extension)
|
||||
|
||||
:param Object obj: object that should be patched
|
||||
:param string patchName: unique string describing the patch
|
||||
:param Object patchValue: an object mapping each key to a patchValue
|
||||
:param Object options: option object (see below)
|
||||
:param object objToPatch: the object that should be patched
|
||||
:param object extension: an object mapping each key to an extension
|
||||
:returns: a function to remove the patch
|
||||
|
||||
The `patch` function modifies in place the `obj` object (or class) and
|
||||
applies all key/value described in the `patchValue` object. This operation
|
||||
is registered under the `patchName` name, so it can be unpatched later if
|
||||
necessary.
|
||||
The `patch` function modifies in place the `objToPatch` object (or class) and
|
||||
applies all key/value described in the `extension` object. An unpatch
|
||||
function is returned, so it can be used to remove the patch later if necessary.
|
||||
|
||||
Most patch operations provide access to the parent value by using the
|
||||
`_super` property (see below in the examples). To do that, the `patch` method
|
||||
wraps each pair key/value in a getter that dynamically binds `_super`.
|
||||
|
||||
The only option is `pure (boolean)`. If set to `true`, the patch operation
|
||||
does not bind the `_super` property.
|
||||
native `super` keyword (see below in the examples).
|
||||
|
||||
Patching a simple object
|
||||
========================
|
||||
@ -53,7 +47,7 @@ Here is a simple example of how an object can be patched:
|
||||
},
|
||||
};
|
||||
|
||||
patch(object, "patch name", {
|
||||
patch(object, {
|
||||
fn() {
|
||||
// do things
|
||||
},
|
||||
@ -61,49 +55,45 @@ Here is a simple example of how an object can be patched:
|
||||
|
||||
|
||||
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``:
|
||||
function. To do so, we can simply use the native ``super`` keyword:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
patch(object, "_super patch", {
|
||||
patch(object, {
|
||||
fn() {
|
||||
this._super(...arguments);
|
||||
super.fn(...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:
|
||||
``super`` can only be used in a method not a function. This means that the
|
||||
following constructs are invalid for javascript.
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
patch(object, "async _super patch", {
|
||||
async myAsyncFn() {
|
||||
const _super = this._super.bind(this);
|
||||
await Promise.resolve();
|
||||
await _super(...arguments);
|
||||
// await this._super(...arguments); // this._super is undefined.
|
||||
const obj = {
|
||||
a: function () {
|
||||
// Throws: "Uncaught SyntaxError: 'super' keyword unexpected here"
|
||||
super.a();
|
||||
},
|
||||
});
|
||||
|
||||
b: () => {
|
||||
// Throws: "Uncaught SyntaxError: 'super' keyword unexpected here"
|
||||
super.b();
|
||||
},
|
||||
};
|
||||
|
||||
Getters and setters are supported too:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
patch(object, "getter/setter patch", {
|
||||
patch(object, {
|
||||
get number() {
|
||||
return this._super() / 2;
|
||||
return super.number / 2;
|
||||
},
|
||||
set number(value) {
|
||||
this._super(value * 2);
|
||||
super.number = value;
|
||||
},
|
||||
});
|
||||
|
||||
@ -124,12 +114,12 @@ the `prototype`:
|
||||
}
|
||||
|
||||
// this will patch static properties!!!
|
||||
patch(MyClass, "static patch", {
|
||||
patch(MyClass, {
|
||||
myStaticFn() {...},
|
||||
});
|
||||
|
||||
// this is probably the usual case: patching a class method
|
||||
patch(MyClass.prototype, "prototype patch", {
|
||||
patch(MyClass.prototype, {
|
||||
myPrototypeFn() {...},
|
||||
});
|
||||
|
||||
@ -149,9 +139,9 @@ constructor and patch that method instead:
|
||||
}
|
||||
}
|
||||
|
||||
patch(MyClass.prototype, "constructor", {
|
||||
patch(MyClass.prototype, {
|
||||
setup() {
|
||||
this._super(...arguments);
|
||||
super.setup(...arguments);
|
||||
this.doubleNumber = this.number * 2;
|
||||
},
|
||||
});
|
||||
@ -165,11 +155,11 @@ Patching a component
|
||||
|
||||
Components are defined by javascript classes, so all the information above still
|
||||
holds. For these reasons, Owl components should use the `setup` method, so they
|
||||
can easily be patched as well (see the section on :ref:`best practices<frontend/owl/best_practices>`.
|
||||
can easily be patched as well (see the section on :ref:`best practices<frontend/owl/best_practices>`).
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
patch(MyComponent.prototype, "my patch", {
|
||||
patch(MyComponent.prototype, {
|
||||
setup() {
|
||||
useMyHook();
|
||||
},
|
||||
@ -178,19 +168,91 @@ can easily be patched as well (see the section on :ref:`best practices<frontend/
|
||||
Removing a patch
|
||||
================
|
||||
|
||||
The `patch` function has a counterpart, `unpatch`, also located in `@web/core/utils/patch`.
|
||||
|
||||
.. js:function:: unpatch(obj, patchName)
|
||||
|
||||
:param Object obj: object that should be unpatched
|
||||
:param string patchName: string describing the patch that should be removed
|
||||
|
||||
Removes an existing patch from an object `obj`. This is mostly useful for
|
||||
The `patch` function returns its counterpart. This is mostly useful for
|
||||
testing purposes, when we patch something at the beginning of a test, and
|
||||
unpatch it at the end.
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
patch(object, "patch name", { ... });
|
||||
const unpatch = patch(object, { ... });
|
||||
// test stuff here
|
||||
unpatch(object, "patch name");
|
||||
unpatch();
|
||||
|
||||
Applying the same patch to multiple objects
|
||||
===========================================
|
||||
|
||||
It could happen that one wants to apply the same patch to multiple objects but
|
||||
because of the way the `super` keyword works, the `extension` can only be used
|
||||
for patching once and cannot be copied/cloned (`check the documentation of the keyword <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/super#description>`_).
|
||||
A function returning the object used to patch can be used to make it unique.
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
const obj1 = {
|
||||
method() {
|
||||
doSomething();
|
||||
},
|
||||
};
|
||||
|
||||
const obj2 = {
|
||||
method() {
|
||||
doThings();
|
||||
},
|
||||
};
|
||||
|
||||
function createExtensionObj() {
|
||||
return {
|
||||
method() {
|
||||
super.method();
|
||||
doCommonThings();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
patch(obj1, createExtensionObj());
|
||||
patch(obj2, createExtensionObj());
|
||||
|
||||
.. warning::
|
||||
|
||||
If an `extension` is based on another then the two extensions should
|
||||
be applied separately. Do not copy/clone an extension.
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
const object = {
|
||||
method1() {
|
||||
doSomething();
|
||||
},
|
||||
method2() {
|
||||
doAnotherThing();
|
||||
},
|
||||
};
|
||||
|
||||
const ext1 = {
|
||||
method1() {
|
||||
super.method1();
|
||||
doThings();
|
||||
},
|
||||
};
|
||||
|
||||
const invalid_ext2 = {
|
||||
...ext1, // this will not work: super will not refer to the correct object in methods coming from ext1
|
||||
method2() {
|
||||
super.method2();
|
||||
doOtherThings();
|
||||
},
|
||||
};
|
||||
|
||||
patch(object, invalid_ext2);
|
||||
object.method1(); // throws: Uncaught TypeError: (intermediate value).method1 is not a function
|
||||
|
||||
const valid_ext2 = {
|
||||
method2() {
|
||||
super.method2();
|
||||
doOtherThings();
|
||||
},
|
||||
};
|
||||
|
||||
patch(object, ext1); // first patch base extension
|
||||
patch(object, valid_ext2); // then the new one
|
||||
object.method1(); // works as expected
|
||||
|
Loading…
Reference in New Issue
Block a user