From 20e4cd5bcd1816a478aedd441efa90a34cf63334 Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Thu, 8 Feb 2018 11:28:45 +0100 Subject: [FIX] web: field widget with readonly modifier Rev. dbdd742 introduced the 'baseMode' parameter of field's nodes in the BasicRenderer. Its purpose is to encode the generic mode of the field, regardless of its readonly modifier (i.e. in form views, the baseMode of all fields is the mode of the renderer and in list views, the baseMode of all fields is 'readonly', except for fields in the row being edited, whose baseMode is 'edit'). Unfortunately, when the value of the readonly modifier of a field was 'readonly', its baseMode was changed from 'edit' to 'readonly', which shouldn't, as the baseMode's value should not be affected by the modifiers. This produced a bug when a field had a readonly modifier, as once its value became true, the field was rendered in 'readonly', and whatever the value of the modifier afterwards, it always remained in 'readonly'. The rev. ensures that the baseMode is no longer impacted by the value of the readonly modifier. Closes #22729 --- .../static/src/js/views/basic/basic_renderer.js | 51 +++++++++++++++------- 1 file changed, 35 insertions(+), 16 deletions(-) (limited to 'addons/web/static/src/js/views/basic/basic_renderer.js') diff --git a/addons/web/static/src/js/views/basic/basic_renderer.js b/addons/web/static/src/js/views/basic/basic_renderer.js index 64b90c33022..f5238a95b33 100644 --- a/addons/web/static/src/js/views/basic/basic_renderer.js +++ b/addons/web/static/src/js/views/basic/basic_renderer.js @@ -254,7 +254,10 @@ var BasicRenderer = AbstractRenderer.extend({ if ('readonly' in modifiers && element.widget) { var mode = modifiers.readonly ? 'readonly' : modifiersData.baseMode; if (mode !== element.widget.mode) { - self._rerenderFieldWidget(element.widget, record, mode); + self._rerenderFieldWidget(element.widget, record, { + keepBaseMode: true, + mode: mode, + }); return; // Rerendering already applied the modifiers, no need to go further } } @@ -406,12 +409,25 @@ var BasicRenderer = AbstractRenderer.extend({ * @param {Object} record * @param {jQuery|AbstractField} [element] * @param {Object} [options] - * @param {Object} [options.callback] - the callback to call on registration - * and on modifiers updates + * @param {Object} [options.callback] the callback to call on registration + * and on modifiers updates + * @param {boolean} [options.keepBaseMode=false] this function registers the + * 'baseMode' of the node; this is a field widget specific settings which + * represents the generic mode of the widget, regardless of its modifiers + * (the interesting case is the list view: all widgets are supposed to be + * in the baseMode 'readonly', except the ones that are in the line that + * is currently being edited). + * With option 'keepBaseMode' set to true, the current baseMode of the + * node isn't overridden (this is particularily useful when a field widget + * is re-rendered because its readonly modifier changed, as in this case, + * we don't want to change its base mode). + * @param {string} [options.mode] the 'baseMode' of the node is set to this + * value (if not given, it is set to this.mode, the mode of the renderer) * @returns {Object} for code efficiency, returns the last evaluated - * modifiers for the given node and record. + * modifiers for the given node and record. */ _registerModifiers: function (node, record, element, options) { + options = options || {}; // Check if we already registered the modifiers for the given node // If yes, this is simply an update of the related element // If not, check the modifiers to see if it needs registration @@ -428,12 +444,11 @@ var BasicRenderer = AbstractRenderer.extend({ this.allModifiersData.push(modifiersData); } } - // we register here the base mode of the node. This is a field widget - // specific settings which represents the generic mode for the widget, - // regardless of its modifiers. The interesting case is the list view: - // all widgets are supposed to be in the baseMode 'readonly', except the - // ones that are in the line that is currently being edited. - modifiersData.baseMode = (options && options.mode) || this.mode; + + // Compute node's base mode if necessary + if (!options.keepBaseMode) { + modifiersData.baseMode = options.mode || this.mode; + } // Evaluate if necessary if (!modifiersData.evaluatedModifiers[record.id]) { @@ -494,13 +509,16 @@ var BasicRenderer = AbstractRenderer.extend({ * @private * @param {Object} node * @param {Object} record - * @param {Object} [options] + * @param {Object} [options] passed to @_registerModifiers + * @param {string} [options.mode] either 'edit' or 'readonly' (defaults to + * this.mode, the mode of the renderer) * @returns {AbstractField} */ _renderFieldWidget: function (node, record, options) { + options = options || {}; var fieldName = node.attrs.name; // Register the node-associated modifiers - var mode = options && options.mode || this.mode; + var mode = options.mode || this.mode; var modifiers = this._registerModifiers(node, record, null, options); // Initialize and register the widget // Readonly status is known as the modifiers have just been registered @@ -537,7 +555,8 @@ var BasicRenderer = AbstractRenderer.extend({ && !element.widget.isSet() )); }, - mode: mode + keepBaseMode: !!options.keepBaseMode, + mode: mode, }); self._postProcessField(widget, node); }); @@ -606,12 +625,12 @@ var BasicRenderer = AbstractRenderer.extend({ * @private * @param {Widget} widget * @param {Object} record - * @param {string} mode either 'readonly' or 'edit' + * @param {Object} [options] options passed to @_renderFieldWidget * @returns {AbstractField} */ - _rerenderFieldWidget: function (widget, record, mode) { + _rerenderFieldWidget: function (widget, record, options) { // Render the new field widget - var newWidget = this._renderFieldWidget(widget.__node, record, {mode: mode}); + var newWidget = this._renderFieldWidget(widget.__node, record, options); widget.$el.replaceWith(newWidget.$el); // Destroy the old widget and position the new one at the old one's -- cgit v1.2.3 From e0cb522bf2bb1c12b2a3cce8c441a93a47685e0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Debongnie?= Date: Fri, 9 Feb 2018 15:47:43 +0100 Subject: [FIX] web: update form widgets when state has changed Since the tag is not used anymore in any of our views, it was not really properly supported. In particular, the form widget was not notified when the state of the form view has changed, which makes it very difficult to actually do any useful work. With this commit, we make sure that the widget method 'updateState' (if it exists) is called with the new state whenever it changed. This should allow developers to do useful work without having to work around the issue. Note that this is not really a perfect solution, since the updateState methot may or may not have been implemented. I think that we need some kind of ViewWidget abstraction, which could be used to have a better and more complete API for widgets in a basic view. And AbstractField could inherit from ViewWidget. --- addons/web/static/src/js/views/basic/basic_renderer.js | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'addons/web/static/src/js/views/basic/basic_renderer.js') diff --git a/addons/web/static/src/js/views/basic/basic_renderer.js b/addons/web/static/src/js/views/basic/basic_renderer.js index f5238a95b33..204c35acaef 100644 --- a/addons/web/static/src/js/views/basic/basic_renderer.js +++ b/addons/web/static/src/js/views/basic/basic_renderer.js @@ -30,6 +30,7 @@ var BasicRenderer = AbstractRenderer.extend({ this.activeActions = params.activeActions; this.viewType = params.viewType; this.mode = params.mode || 'readonly'; + this.widgets = []; }, /** * This method has two responsabilities: find every invalid fields in the @@ -95,6 +96,9 @@ var BasicRenderer = AbstractRenderer.extend({ return this._render().then(_.constant([])); } + // reset all widgets (from the tag) if any: + _.invoke(this.widgets, 'updateState', state); + var defs = []; // Reset all the field widgets that are marked as changed and the ones @@ -605,6 +609,8 @@ var BasicRenderer = AbstractRenderer.extend({ var Widget = widgetRegistry.get(node.attrs.name); var widget = new Widget(this, record); + this.widgets.push(widget); + // Prepare widget rendering and save the related deferred var def = widget.__widgetRenderAndInsert(function () {}); if (def.state() === 'pending') { -- cgit v1.2.3