[FIX] web: prevent navigation when line is invalid in o2m

The issue that we fix in this commit is the following: open a form view
with a o2m with one required field, and the o2m is editable.  When
adding a new line, it is invalid (since the field with required), but
when pressing ENTER, the editable list tries to navigate away, which
will discard the current line (in the unselectRow method), before
completing the move_to_next_line.  So, at this point, the list editable
create a new (empty) record, and we are back to square one, but with a
visible flicker and no information on what happened.

To fix this issue, we simply check that the renderer can be saved when
moving to next line.  If it is not (the line is invalid), then we simply
abort the move.

Notes:
* this behaviour worked fine in list view, because the canBeSaved check
  is made by the saveRecord method in listController
* it looks like the protection should be put into unselectRow, but this is
  not true (at least, in the current state): in some cases, calling
  unselectRow with invalid state should result in discarding the current row
  (in a new line in a o2m, when the record is not dirty, for example), and
  the list editable renderer does not know if this is the case or not.
  However, if the unselect action comes from a navigation event, then we want
  to avoid discarding the current row.
This commit is contained in:
Géry Debongnie
2017-05-22 10:07:32 +02:00
parent 8436150ae5
commit a60ae43835
2 changed files with 44 additions and 1 deletions
@@ -251,6 +251,12 @@ ListRenderer.include({
* @private
*/
_moveToNextLine: function () {
var record = this.state.data[this.currentRow];
var fieldNames = this.canBeSaved(record.id);
if (fieldNames.length) {
return;
}
if (this.currentRow < this.state.data.length - 1) {
this._selectCell(this.currentRow + 1, 0);
} else {
@@ -816,7 +816,6 @@ QUnit.module('relational_fields', {
});
});
QUnit.module('FieldOne2Many');
QUnit.test('one2many basic properties', function (assert) {
@@ -1721,6 +1720,44 @@ QUnit.module('relational_fields', {
form.destroy();
});
QUnit.test('pressing enter in a o2m with a required empty m2o', function (assert) {
assert.expect(4);
this.data.turtle.fields.turtle_foo.required = true;
var form = createView({
View: FormView,
model: 'partner',
data: this.data,
arch: '<form string="Partners">' +
'<group>' +
'<field name="turtles">' +
'<tree editable="top">' +
'<field name="turtle_foo"/>' +
'</tree>' +
'</field>' +
'</group>' +
'</form>',
res_id: 2,
mockRPC: function (route, args) {
assert.step(args.method);
return this._super.apply(this, arguments);
},
});
// edit mode, then click on Add an item, then click elsewhere
form.$buttons.find('.o_form_button_edit').click();
form.$('.o_field_x2many_list_row_add a').click();
form.$('input[name="turtle_foo"]').trigger($.Event('keydown', {
which: $.ui.keyCode.ENTER,
keyCode: $.ui.keyCode.ENTER,
}));
assert.ok(form.$('input[name="turtle_foo"]').hasClass('o_field_invalid'),
"input should be marked invalid");
assert.verifySteps(['read', 'default_get']);
form.destroy();
});
QUnit.test('editing a o2m, with required field and onchange', function (assert) {
assert.expect(12);