From 4f29bebb02bea2827bead4a42ba30ad4b57ef3f6 Mon Sep 17 00:00:00 2001 From: "Louis (loco)" Date: Tue, 9 Jan 2024 15:03:55 +0100 Subject: [PATCH] [FIX] sale: ensure widget is alive before executing code Steps to reproduce the bug: - Add a delay (for example 5 seconds) at the beginning of the `/sale/get_combination_info_website` route. - Add a delay (for example 10 seconds) in a `willStart` method in the `Wysiwyg`. - Go on a product page and enter edit mode. - Click on the the product image. -> The "Replace" button does not appear. In this situation, the added delays represent slow rpc answers. Let's analyse the flow of instructions in order to better understand the problem: - When you go on a product page, the `WebsiteSale` public widget is started. `_getCombinationInfo` of `VariantMixin` is then called through the `start()` of the public widget. As the rpc is taking time to answer, `_onChangeCombination()` is not directly called. - When entering edit mode, the `WebsiteSale` public widget is destroyed. Due to the edit mode, the `o_editable` class has been added on editable elements. - After the first added delay and thanks to the rpc answer, `_updateProductImage()` is called through `_onChangeCombination()`. Because the `editor_enable` class has not been added to the document body yet (due to the second added rpc), the `_updateProductImage()` method replaces some elements of the DOM and by doing so, removes the `o_editable` class of some elements. Consequently, the snippet option linked to the image is not displayed. The problem here is that the `_updateProductImage` method is called even if its associated public widget has been destroyed. To solve the problem, we first check that the associated widget is alive before handling the result of the rpc answer in the `VariantMixin` mixin. Note that the first idea was to use `this._rpc()` instead of `ajax.jsonRpc()` in the mixin. Indeed, the advantage of using `this._rpc()` is that it already ensures that the associated widget is alive before handling the result of the rpc answer. The problem is that some widgets that use the `VariantMixin` are created in such a way that `this._rpc()` can not be used on them (for example `OptionalProductsModal`). Related to runbot-28700 closes odoo/odoo#149145 X-original-commit: 599e112ba7580617c61226b73355035c89af3395 Signed-off-by: Quentin Smetz (qsm) --- .../static/src/js/sale_variant_mixin.js | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/addons/website_sale/static/src/js/sale_variant_mixin.js b/addons/website_sale/static/src/js/sale_variant_mixin.js index 88769fe2e55..0eb37375624 100644 --- a/addons/website_sale/static/src/js/sale_variant_mixin.js +++ b/addons/website_sale/static/src/js/sale_variant_mixin.js @@ -79,6 +79,9 @@ var VariantMixin = { 'context': this.context, ...this._getOptionalCombinationInfoParam($currentOptionalProduct), }).then((combinationData) => { + if (this._shouldIgnoreRpcResult()) { + return; + } this._onChangeCombination(ev, $currentOptionalProduct, combinationData); this._checkExclusions($currentOptionalProduct, childCombination, combinationData.parent_exclusions); }); @@ -98,6 +101,9 @@ var VariantMixin = { 'context': this.context, ...this._getOptionalCombinationInfoParam($parent), }).then((combinationData) => { + if (this._shouldIgnoreRpcResult()) { + return; + } this._onChangeCombination(ev, $parent, combinationData); this._checkExclusions($parent, combination, combinationData.parent_exclusions); }); @@ -721,6 +727,18 @@ var VariantMixin = { .addClass("active"); }, + /** + * Return true if the current object has been destroyed. + * This function has been added as a fix to know if the result of a rpc + * should be handled. Indeed, "this._rpc()" can not be used as it is not + * supported by some elements that use this mixin. + * + * @private + */ + _shouldIgnoreRpcResult() { + return (typeof this.isDestroyed === "function" && this.isDestroyed()); + }, + /** * Extension point for website_sale *