From 241704536d38f387d076c73c503b36b062a91ff7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Theys?= Date: Tue, 21 Aug 2018 16:13:23 +0200 Subject: [PATCH] [FIX] web: allow o2m navigation to work from required inputs In a one2many, pressing ENTER or TAB from a required input (of type text or char) that is on the last editable column of its line would fail to navigate to next line. The issue was that the validation of the required field would happen before the change to its value was actually applied. This commit fixes it. PR: #26430 Task: 1869469 --- .../web/static/src/js/fields/basic_fields.js | 4 +- .../static/src/js/fields/relational_fields.js | 8 +++ .../js/views/list/list_editable_renderer.js | 28 ++++---- .../tests/fields/relational_fields_tests.js | 66 +++++++++++++++++++ 4 files changed, 91 insertions(+), 15 deletions(-) diff --git a/addons/web/static/src/js/fields/basic_fields.js b/addons/web/static/src/js/fields/basic_fields.js index 71b2a14c434..fdefb2cf967 100644 --- a/addons/web/static/src/js/fields/basic_fields.js +++ b/addons/web/static/src/js/fields/basic_fields.js @@ -114,7 +114,7 @@ var DebouncedField = AbstractField.extend({ */ commitChanges: function () { if (this._isDirty && this.mode === 'edit') { - this._doAction(); + return this._doAction(); } }, @@ -135,7 +135,7 @@ var DebouncedField = AbstractField.extend({ // do anything (commitChanges ensures that if it has local changes, they // are triggered up before the widget is destroyed, if necessary). if (!this.isDestroyed()) { - this._setValue(this._getValue()); + return this._setValue(this._getValue()); } }, /** diff --git a/addons/web/static/src/js/fields/relational_fields.js b/addons/web/static/src/js/fields/relational_fields.js index f3125c72f29..7683cd6db94 100644 --- a/addons/web/static/src/js/fields/relational_fields.js +++ b/addons/web/static/src/js/fields/relational_fields.js @@ -1124,6 +1124,14 @@ var FieldX2Many = AbstractField.extend({ operation: 'UPDATE', id: ev.data.dataPointID, data: changes, + }).done(function () { + if (ev.data.onSuccess) { + ev.data.onSuccess(); + } + }).fail(function () { + if (ev.data.onFailure) { + ev.data.onFailure(); + } }); } }, diff --git a/addons/web/static/src/js/views/list/list_editable_renderer.js b/addons/web/static/src/js/views/list/list_editable_renderer.js index 5c33a32b6d5..37bd7b153c7 100644 --- a/addons/web/static/src/js/views/list/list_editable_renderer.js +++ b/addons/web/static/src/js/views/list/list_editable_renderer.js @@ -487,22 +487,24 @@ ListRenderer.include({ * @private */ _moveToNextLine: function () { + var self = this; var record = this.state.data[this.currentRow]; - var fieldNames = this.canBeSaved(record.id); - if (fieldNames.length) { - return; - } + this.commitChanges(record.id).then(function () { + var fieldNames = self.canBeSaved(record.id); + if (fieldNames.length) { + return; + } - if (this.currentRow < this.state.data.length - 1) { - this._selectCell(this.currentRow + 1, 0); - } else { - var self = this; - this.unselectRow().then(function () { - self.trigger_up('add_record', { - onFail: self._selectCell.bind(self, 0, 0, {}), + if (self.currentRow < self.state.data.length - 1) { + self._selectCell(self.currentRow + 1, 0); + } else { + self.unselectRow().then(function () { + self.trigger_up('add_record', { + onFail: self._selectCell.bind(self, 0, 0, {}), + }); }); - }); - } + } + }); }, /** * @override diff --git a/addons/web/static/tests/fields/relational_fields_tests.js b/addons/web/static/tests/fields/relational_fields_tests.js index 245ff8f7a07..77b2c403b8c 100644 --- a/addons/web/static/tests/fields/relational_fields_tests.js +++ b/addons/web/static/tests/fields/relational_fields_tests.js @@ -10168,6 +10168,72 @@ QUnit.module('relational_fields', { form.destroy(); }); + QUnit.test('one2many with onchange, required field, shortcut enter', function (assert) { + assert.expect(5); + + this.data.turtle.onchanges = { + turtle_foo: function () {}, + }; + + var def; + var form = createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '
' + + '' + + '' + + '' + + '' + + '' + + '
', + mockRPC: function (route, args) { + var result = this._super.apply(this, arguments); + if (args.method === 'onchange') { + return $.when(def).then(_.constant(result)); + } + return result; + }, + // simulate what happens in the client: + // the new value isn't notified directly to the model + fieldDebounce: 5000, + }); + + var value = "hello"; + + // add a new line + form.$('.o_field_x2many_list_row_add a').click(); + + // we want to add a delay to simulate an onchange + def = $.Deferred(); + + // write something in the field + var $input = form.$('input[name="turtle_foo"]'); + $input.val(value).trigger('input'); + + // trigger keydown ENTER + $input.trigger($.Event('keydown', { + keyCode: $.ui.keyCode.ENTER, + which: $.ui.keyCode.ENTER, + })); + + // check that nothing changed before the onchange finished + assert.strictEqual($input.val(), value, "input content shouldn't change"); + assert.strictEqual(form.$('.o_data_row').length, 1, + "should still contain only one row"); + + // unlock onchange + def.resolve(); + + // check the current line is added with the correct content and a new line is editable + assert.strictEqual(form.$('td.o_data_cell').text(), value); + assert.strictEqual(form.$('input[name="turtle_foo"]').val(), ''); + assert.strictEqual(form.$('.o_data_row').length, 2, + "should now contain two rows"); + + form.destroy(); + }); + QUnit.module('FieldMany2Many'); QUnit.test('many2many kanban: edition', function (assert) {