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 diff --git a/addons/web/static/tests/views/form_tests.js b/addons/web/static/tests/views/form_tests.js index ad7a8cb4672..a4952644e15 100644 --- a/addons/web/static/tests/views/form_tests.js +++ b/addons/web/static/tests/views/form_tests.js @@ -630,7 +630,7 @@ QUnit.module('Views', { }); QUnit.test('readonly attrs on fields are re-evaluated on field change', function (assert) { - assert.expect(3); + assert.expect(4); var form = createView({ View: FormView, @@ -656,6 +656,9 @@ QUnit.module('Views', { form.$('.o_field_boolean input').click(); assert.strictEqual(form.$('span[name="foo"]').length, 1, "the foo field widget should have been rerendered to now be readonly again"); + form.$('.o_field_boolean input').click(); + assert.strictEqual(form.$('input[name="foo"]').length, 1, + "the foo field widget should have been rerendered to now be editable again"); form.destroy(); }); diff --git a/addons/web/static/tests/views/list_tests.js b/addons/web/static/tests/views/list_tests.js index e282d1186e0..2fafef26c6f 100644 --- a/addons/web/static/tests/views/list_tests.js +++ b/addons/web/static/tests/views/list_tests.js @@ -1929,7 +1929,7 @@ QUnit.module('Views', { }); QUnit.test('readonly attrs on fields are re-evaluated on field change', function (assert) { - assert.expect(7); + assert.expect(9); var list = createView({ View: ListView, @@ -1963,9 +1963,14 @@ QUnit.module('Views', { assert.strictEqual(list.$('tbody td.o_readonly_modifier').length, 3, "the foo field widget parent cell should now be readonly again"); - // Reswitch the cell to editable and save the row list.$('tbody tr:nth(0) td:nth(2) input').click(); - list.$('thead').click(); + assert.strictEqual(list.$('tbody tr:nth(0) td:nth(1) > input[name="foo"]').length, 1, + "the foo field widget should have been rerendered as editable again"); + assert.strictEqual(list.$('tbody td.o_readonly_modifier').length, 2, + "the foo field widget parent cell should not be readonly again"); + + // Click outside to leave edition mode + list.$el.click(); assert.strictEqual(list.$('tbody td.o_readonly_modifier').length, 2, "there should be 2 readonly foo cells in readonly mode");