diff options
| author | Christophe Matthieu <chm@odoo.com> | 2017-08-24 15:27:44 +0200 |
|---|---|---|
| committer | Christophe Matthieu <chm@odoo.com> | 2017-08-30 14:39:17 +0200 |
| commit | 2728fe1ec1ac7e99819d0f30f29a1814b00c82a4 (patch) | |
| tree | 89b1325b1ba3e86bc6ac830462c4694bc99553d2 /addons/web/static/src/js | |
| parent | 6cf98c4c87d644928897a76b9985947ab2ebc607 (diff) | |
[IMP] web: onchange x2many with virtual id in create command
When an onchange modifies an x2many field, the line on which the user
works remains in editable mode and the cursor is returned to the same
position.
The second argument of create command is a reference used to reactivate
the edited line.
Diffstat (limited to 'addons/web/static/src/js')
| -rw-r--r-- | addons/web/static/src/js/core/dom.js | 42 | ||||
| -rw-r--r-- | addons/web/static/src/js/fields/relational_fields.js | 26 | ||||
| -rw-r--r-- | addons/web/static/src/js/views/basic/basic_model.js | 13 | ||||
| -rw-r--r-- | addons/web/static/src/js/views/basic/basic_renderer.js | 16 |
4 files changed, 89 insertions, 8 deletions
diff --git a/addons/web/static/src/js/core/dom.js b/addons/web/static/src/js/core/dom.js index 6650022c486..7bae1bc29a5 100644 --- a/addons/web/static/src/js/core/dom.js +++ b/addons/web/static/src/js/core/dom.js @@ -153,10 +153,32 @@ return { return $to_detach.detach(); }, /** + * Returns the input or textarea cursor offset + * + * @param {Object} DOM item input or texteara + * @returns {Object} + */ + getCursor: function (node) { + node = $(node)[0]; + var offset = 0; + var length = 0; + if ('selectionStart' in node) { + offset = node.selectionStart; + length = node.selectionEnd - offset; + } else if ('selection' in document) { + node.focus(); + var sel = document.selection.createRange(); + length = document.selection.createRange().text.length; + sel.moveStart('character', -node.value.length); + offset = sel.text.length - length; + } + return { offset: offset, length: length }; + }, + /** * Returns the distance between a DOM element and the top-left corner of the * window * - * @param {element} [e] the DOM element + * @param {Object} node DOM element (input or texteara) * @return {Object} the left and top distances in pixels */ getPosition: function (e) { @@ -254,5 +276,23 @@ return { } return $container; }, + /** + * Moves the cursor position in a DOM element (input or texteara) + * + * @param {Object} node DOM element (input or textarea) + * @param {Int} pos cursor position + */ + setCursor: function (node, pos) { + node = $(node).first().focus()[0]; + if ('createTextRange' in node) { + var textRange = node.createTextRange(); + textRange.collapse(true); + textRange.moveEnd(pos); + textRange.moveStart(pos); + textRange.select(); + } else if ('setSelectionRange' in node) { + node.setSelectionRange(pos, pos); + } + }, }; }); diff --git a/addons/web/static/src/js/fields/relational_fields.js b/addons/web/static/src/js/fields/relational_fields.js index 301717d985d..ba9b947d6a3 100644 --- a/addons/web/static/src/js/fields/relational_fields.js +++ b/addons/web/static/src/js/fields/relational_fields.js @@ -20,6 +20,7 @@ var ControlPanel = require('web.ControlPanel'); var dialogs = require('web.view_dialogs'); var core = require('web.core'); var data = require('web.data'); +var dom = require('web.dom'); var Dialog = require('web.Dialog'); var KanbanRenderer = require('web.KanbanRenderer'); var ListRenderer = require('web.ListRenderer'); @@ -684,6 +685,7 @@ var FieldX2Many = AbstractField.extend({ if (command.operation === 'UPDATE' && command.data) { var state = record.data[this.name]; var fieldNames = state.getFieldNames(); + this._reset(record, ev); this.renderer.confirmChange(state, command.id, fieldNames, ev.initialEvent); return $.when(); } @@ -895,11 +897,33 @@ var FieldX2Many = AbstractField.extend({ this.lastInitialEvent = undefined; if (Object.keys(changes).length) { this.lastInitialEvent = ev; - this._setValue({ + // store the cursor position to restore it once potential onchanges have been applied + var self = this; + var editableID = this.renderer.getEditableRecordID(); + var datapoint = _.find(this.value.data, {id: editableID}); + + var ref = datapoint && datapoint.ref; + var cursor = ref && dom.getCursor(ev.target.getFocusableElement()); + var fieldName = ev.target.name; + + var def = this._setValue({ operation: 'UPDATE', id: ev.data.dataPointID, data: changes, }); + + if (ref) { + def.then(function () { + var nextEditableRecordID = self.renderer.getEditableRecordID(); + if (nextEditableRecordID && editableID !== nextEditableRecordID) { + return; + } + var datapoint = _.find(self.value.data, {ref: ref}); + if (datapoint) { + self.renderer.focusField(datapoint.id, fieldName, cursor && cursor.offset); + } + }); + } } }, /** diff --git a/addons/web/static/src/js/views/basic/basic_model.js b/addons/web/static/src/js/views/basic/basic_model.js index 0d95c292070..537fa11fdb9 100644 --- a/addons/web/static/src/js/views/basic/basic_model.js +++ b/addons/web/static/src/js/views/basic/basic_model.js @@ -92,10 +92,10 @@ var session = require('web.session'); var _t = core._t; var x2ManyCommands = { - // (0, _, {values}) + // (0, virtualID, {values}) CREATE: 0, - create: function (values) { - return [x2ManyCommands.CREATE, false, values]; + create: function (virtualID, values) { + return [x2ManyCommands.CREATE, virtualID || false, values]; }, // (1, id, {values}) UPDATE: 1, @@ -391,6 +391,7 @@ var BasicModel = AbstractModel.extend({ limit: element.limit, model: element.model, offset: element.offset, + ref: element.ref, res_ids: element.res_ids.slice(0), specialData: _.extend({}, element.specialData), type: 'record', @@ -1213,6 +1214,7 @@ var BasicModel = AbstractModel.extend({ modelName: list.model, parentID: list.id, viewType: list.viewType, + ref: command[1], }; if (command[0] === 1) { params.res_id = command[1]; @@ -2415,7 +2417,7 @@ var BasicModel = AbstractModel.extend({ commands[fieldName].push(x2ManyCommands.replace_with(realIDs)); _.each(relRecordCreated, function (relRecord) { var changes = self._generateChanges(relRecord, options); - commands[fieldName].push(x2ManyCommands.create(changes)); + commands[fieldName].push(x2ManyCommands.create(relRecord.ref, changes)); }); // generate update commands for records that have been // updated (it may happen with editable lists) @@ -2451,7 +2453,7 @@ var BasicModel = AbstractModel.extend({ // this is a new id relRecord = _.findWhere(relRecordAdded, {res_id: list.res_ids[i]}); changes = this._generateChanges(relRecord, options); - commands[fieldName].push(x2ManyCommands.create(changes)); + commands[fieldName].push(x2ManyCommands.create(relRecord.ref, changes)); } } if (options.changesOnly && !didChange && addedIds.length === 0 && removedIds.length === 0) { @@ -2824,6 +2826,7 @@ var BasicModel = AbstractModel.extend({ orderedBy: params.orderedBy || [], parentID: params.parentID, rawContext: params.rawContext, + ref: params.ref || res_id, relationField: params.relationField, res_id: res_id, res_ids: res_ids, 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 e040a5fa7f0..0e26e2fa19f 100644 --- a/addons/web/static/src/js/views/basic/basic_renderer.js +++ b/addons/web/static/src/js/views/basic/basic_renderer.js @@ -10,7 +10,7 @@ odoo.define('web.BasicRenderer', function (require) { var AbstractRenderer = require('web.AbstractRenderer'); var config = require('web.config'); var core = require('web.core'); -var Domain = require('web.Domain'); +var dom = require('web.dom'); var qweb = core.qweb; @@ -115,6 +115,20 @@ var BasicRenderer = AbstractRenderer.extend({ return resetWidgets; }); }, + /** + * Activates the widget and move the cursor to the given offset + * + * @param {string} id + * @param {string} fieldName + * @param {integer} offset + */ + focusField: function (id, fieldName, offset) { + this.editRecord(id); + if (typeof offset === "number") { + var field = _.findWhere(this.allFieldWidgets[id], {name: fieldName}); + dom.setCursor(field.getFocusableElement(), offset); + } + }, //-------------------------------------------------------------------------- // Private |
