[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
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user