Skip to content

Commit 775e80e

Browse files
committed
[IMP] web: update documentation of patch function
This commit updates the documentation of the patch function with the changes done in odoo/odoo#125716. closes #5017 Signed-off-by: Antoine Vandevenne (anv) <anv@odoo.com>
1 parent dad4a47 commit 775e80e

File tree

1 file changed

+117
-55
lines changed

1 file changed

+117
-55
lines changed

content/developer/reference/frontend/patching_code.rst

Lines changed: 117 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -18,24 +18,18 @@ Description
1818

1919
The patch function is located in `@web/core/utils/patch`:
2020

21-
.. js:function:: patch(obj, patchName, patchValue, options)
21+
.. js:function:: patch(objToPatch, extension)
2222

23-
:param Object obj: object that should be patched
24-
:param string patchName: unique string describing the patch
25-
:param Object patchValue: an object mapping each key to a patchValue
26-
:param Object options: option object (see below)
23+
:param object objToPatch: the object that should be patched
24+
:param object extension: an object mapping each key to an extension
25+
:returns: a function to remove the patch
2726

28-
The `patch` function modifies in place the `obj` object (or class) and
29-
applies all key/value described in the `patchValue` object. This operation
30-
is registered under the `patchName` name, so it can be unpatched later if
31-
necessary.
27+
The `patch` function modifies in place the `objToPatch` object (or class) and
28+
applies all key/value described in the `extension` object. An unpatch
29+
function is returned, so it can be used to remove the patch later if necessary.
3230

3331
Most patch operations provide access to the parent value by using the
34-
`_super` property (see below in the examples). To do that, the `patch` method
35-
wraps each pair key/value in a getter that dynamically binds `_super`.
36-
37-
The only option is `pure (boolean)`. If set to `true`, the patch operation
38-
does not bind the `_super` property.
32+
native `super` keyword (see below in the examples).
3933

4034
Patching a simple object
4135
========================
@@ -53,57 +47,53 @@ Here is a simple example of how an object can be patched:
5347
},
5448
};
5549
56-
patch(object, "patch name", {
50+
patch(object, {
5751
fn() {
5852
// do things
5953
},
6054
});
6155
6256
6357
When patching functions, we usually want to be able to access the ``parent``
64-
function. Since we are working with patch objects, not ES6 classes, we cannot
65-
use the native ``super`` keyword. So, Odoo provides a special method to simulate
66-
this behaviour: ``this._super``:
58+
function. To do so, we can simply use the native ``super`` keyword:
6759

6860
.. code-block:: javascript
6961
70-
patch(object, "_super patch", {
62+
patch(object, {
7163
fn() {
72-
this._super(...arguments);
64+
super.fn(...arguments);
7365
// do other things
7466
},
7567
});
7668
7769
.. warning::
7870

79-
``this._super`` is reassigned after each patched function is called.
80-
This means that if you use an asynchronous function in the patch then you
81-
cannot call ``this._super`` after an ``await``, because it may or may not be
82-
the function that you expect. The correct way to do that is to keep a reference
83-
to the initial ``_super`` method:
84-
85-
.. code-block:: javascript
71+
``super`` can only be used in a method not a function. This means that the
72+
following constructs are invalid for javascript.
8673

87-
patch(object, "async _super patch", {
88-
async myAsyncFn() {
89-
const _super = this._super.bind(this);
90-
await Promise.resolve();
91-
await _super(...arguments);
92-
// await this._super(...arguments); // this._super is undefined.
93-
},
94-
});
74+
.. code-block:: javascript
9575
76+
const obj = {
77+
a: function () {
78+
// Throws: "Uncaught SyntaxError: 'super' keyword unexpected here"
79+
super.a();
80+
},
81+
b: () => {
82+
// Throws: "Uncaught SyntaxError: 'super' keyword unexpected here"
83+
super.b();
84+
},
85+
};
9686
9787
Getters and setters are supported too:
9888

9989
.. code-block:: javascript
10090
101-
patch(object, "getter/setter patch", {
91+
patch(object, {
10292
get number() {
103-
return this._super() / 2;
93+
return super.number / 2;
10494
},
10595
set number(value) {
106-
this._super(value * 2);
96+
super.number = value;
10797
},
10898
});
10999
@@ -124,12 +114,12 @@ the `prototype`:
124114
}
125115
126116
// this will patch static properties!!!
127-
patch(MyClass, "static patch", {
117+
patch(MyClass, {
128118
myStaticFn() {...},
129119
});
130120
131121
// this is probably the usual case: patching a class method
132-
patch(MyClass.prototype, "prototype patch", {
122+
patch(MyClass.prototype, {
133123
myPrototypeFn() {...},
134124
});
135125
@@ -149,9 +139,9 @@ constructor and patch that method instead:
149139
}
150140
}
151141
152-
patch(MyClass.prototype, "constructor", {
142+
patch(MyClass.prototype, {
153143
setup() {
154-
this._super(...arguments);
144+
super.setup(...arguments);
155145
this.doubleNumber = this.number * 2;
156146
},
157147
});
@@ -165,11 +155,11 @@ Patching a component
165155

166156
Components are defined by javascript classes, so all the information above still
167157
holds. For these reasons, Owl components should use the `setup` method, so they
168-
can easily be patched as well (see the section on :ref:`best practices<frontend/owl/best_practices>`.
158+
can easily be patched as well (see the section on :ref:`best practices<frontend/owl/best_practices>`).
169159

170160
.. code-block:: javascript
171161
172-
patch(MyComponent.prototype, "my patch", {
162+
patch(MyComponent.prototype, {
173163
setup() {
174164
useMyHook();
175165
},
@@ -178,19 +168,91 @@ can easily be patched as well (see the section on :ref:`best practices<frontend/
178168
Removing a patch
179169
================
180170

181-
The `patch` function has a counterpart, `unpatch`, also located in `@web/core/utils/patch`.
171+
The `patch` function returns its counterpart. This is mostly useful for
172+
testing purposes, when we patch something at the beginning of a test, and
173+
unpatch it at the end.
182174

183-
.. js:function:: unpatch(obj, patchName)
175+
.. code-block:: javascript
184176
185-
:param Object obj: object that should be unpatched
186-
:param string patchName: string describing the patch that should be removed
177+
const unpatch = patch(object, { ... });
178+
// test stuff here
179+
unpatch();
187180
188-
Removes an existing patch from an object `obj`. This is mostly useful for
189-
testing purposes, when we patch something at the beginning of a test, and
190-
unpatch it at the end.
181+
Applying the same patch to multiple objects
182+
===========================================
191183

192-
.. code-block:: javascript
184+
It could happen that one wants to apply the same patch to multiple objects but
185+
because of the way the `super` keyword works, the `extension` can only be used
186+
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>`_).
187+
A function returning the object used to patch can be used to make it unique.
188+
189+
.. code-block:: javascript
190+
191+
const obj1 = {
192+
method() {
193+
doSomething();
194+
},
195+
};
196+
197+
const obj2 = {
198+
method() {
199+
doThings();
200+
},
201+
};
202+
203+
function createExtensionObj() {
204+
return {
205+
method() {
206+
super.method();
207+
doCommonThings();
208+
},
209+
};
210+
}
211+
212+
patch(obj1, createExtensionObj());
213+
patch(obj2, createExtensionObj());
214+
215+
.. warning::
216+
217+
If an `extension` is based on another then the two extensions should
218+
be applied separately. Do not copy/clone an extension.
219+
220+
.. code-block:: javascript
193221
194-
patch(object, "patch name", { ... });
195-
// test stuff here
196-
unpatch(object, "patch name");
222+
const object = {
223+
method1() {
224+
doSomething();
225+
},
226+
method2() {
227+
doAnotherThing();
228+
},
229+
};
230+
231+
const ext1 = {
232+
method1() {
233+
super.method1();
234+
doThings();
235+
},
236+
};
237+
238+
const invalid_ext2 = {
239+
...ext1, // this will not work: super will not refer to the correct object in methods coming from ext1
240+
method2() {
241+
super.method2();
242+
doOtherThings();
243+
},
244+
};
245+
246+
patch(object, invalid_ext2);
247+
object.method1(); // throws: Uncaught TypeError: (intermediate value).method1 is not a function
248+
249+
const valid_ext2 = {
250+
method2() {
251+
super.method2();
252+
doOtherThings();
253+
},
254+
};
255+
256+
patch(object, ext1); // first patch base extension
257+
patch(object, valid_ext2); // then the new one
258+
object.method1(); // works as expected

0 commit comments

Comments
 (0)