From 03fe95d6af9d96f961c47d9acfaec498966b8cfa Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Mon, 22 Oct 2018 11:03:17 +0000 Subject: [REF] web: small refactoring pass on some web components (mainly views and ActionManager) - add missing docstrings - fix typos - correctly order functions and imports - put functions in correct sections - stop using 'event' as argument of handlers - don't call handlers directly - remove trailing spaces/tabs - move module created in stable version to its own file - follow js guidelines in general closes odoo/odoo#28024 --- .../static/src/js/views/basic/basic_controller.js | 30 +- .../web/static/src/js/views/basic/basic_model.js | 360 ++++++++++----------- .../static/src/js/views/basic/basic_renderer.js | 42 +-- 3 files changed, 216 insertions(+), 216 deletions(-) (limited to 'addons/web/static/src/js/views/basic') diff --git a/addons/web/static/src/js/views/basic/basic_controller.js b/addons/web/static/src/js/views/basic/basic_controller.js index 43d604eac0b..0aff337dcbf 100644 --- a/addons/web/static/src/js/views/basic/basic_controller.js +++ b/addons/web/static/src/js/views/basic/basic_controller.js @@ -554,16 +554,16 @@ var BasicController = AbstractController.extend(FieldManagerMixin, { * * @todo: rename db_id into handle * - * @param {OdooEvent} event - * @param {Object} event.data - * @param {string} [event.data.db_id] handle of the data to reload and + * @param {OdooEvent} ev + * @param {Object} ev.data + * @param {string} [ev.data.db_id] handle of the data to reload and * re-render (reload the whole form by default) - * @param {string[]} [event.data.fieldNames] list of the record's fields to + * @param {string[]} [ev.data.fieldNames] list of the record's fields to * reload */ - _onReload: function (event) { - event.stopPropagation(); // prevent other controllers from handling this request - var data = event && event.data || {}; + _onReload: function (ev) { + ev.stopPropagation(); // prevent other controllers from handling this request + var data = ev && ev.data || {}; var handle = data.db_id; if (handle) { // reload the relational field given its db_id @@ -589,28 +589,28 @@ var BasicController = AbstractController.extend(FieldManagerMixin, { * performed through the sidebar. * * @private - * @param {OdooEvent} event + * @param {OdooEvent} ev */ - _onSidebarDataAsked: function (event) { + _onSidebarDataAsked: function (ev) { var sidebarEnv = this._getSidebarEnv(); - event.data.callback(sidebarEnv); + ev.data.callback(sidebarEnv); }, /** * open the translation view for the current field * * @private - * @param {OdooEvent} event + * @param {OdooEvent} ev */ - _onTranslate: function (event) { - event.stopPropagation(); + _onTranslate: function (ev) { + ev.stopPropagation(); var self = this; - var record = this.model.get(event.data.id, {raw: true}); + var record = this.model.get(ev.data.id, {raw: true}); this._rpc({ route: '/web/dataset/call_button', params: { model: 'ir.translation', method: 'translate_fields', - args: [record.model, record.res_id, event.data.fieldName, record.getContext()], + args: [record.model, record.res_id, ev.data.fieldName, record.getContext()], } }).then(function (result) { self.do_action(result, { 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 de879018ad5..f8d6daf081d 100644 --- a/addons/web/static/src/js/views/basic/basic_model.js +++ b/addons/web/static/src/js/views/basic/basic_model.js @@ -201,6 +201,27 @@ var BasicModel = AbstractModel.extend({ return id; }); }, + /** + * Completes the fields and fieldsInfo of a dataPoint with the given ones. + * It is useful for the cases where a record element is shared between + * various views, such as a one2many with a tree and a form view. + * + * @param {string} recordID a valid element ID + * @param {Object} viewInfo + * @param {Object} viewInfo.fields + * @param {Object} viewInfo.fieldsInfo + */ + addFieldsInfo: function (recordID, viewInfo) { + var record = this.localData[recordID]; + record.fields = _.extend({}, record.fields, viewInfo.fields); + // complete the given fieldsInfo with the fields of the main view, so + // that those field will be reloaded if a reload is triggered by the + // secondary view + var fieldsInfo = _.mapObject(viewInfo.fieldsInfo, function (fieldsInfo) { + return _.defaults({}, fieldsInfo, record.fieldsInfo[record.viewType]); + }); + record.fieldsInfo = _.extend({}, record.fieldsInfo, fieldsInfo); + }, /** * Add and process default values for a given record. Those values are * parsed and stored in the '_changes' key of the record. For relational @@ -309,108 +330,52 @@ var BasicModel = AbstractModel.extend({ }); }, /** - * Compute the default value that the handle field should take. - * We need to compute this in order for new lines to be added at the correct position. + * Returns true if a record can be abandoned. * - * @private - * @param {Object} listID - * @param {string} position - * @return {Object} empty object if no overrie has to be done, or: - * field: the name of the field to override, - * value: the value to use for that field + * Case for not abandoning the record: + * + * 1. flagged as 'no abandon' (i.e. during a `default_get`, including any + * `onchange` from a `default_get`) + * 2. registered in a list on addition + * + * 2.1. registered as non-new addition + * 2.2. registered as new additon on update + * + * 3. record is not new + * + * Otherwise, the record can be abandoned. + * + * This is useful when discarding changes on this record, as it means that + * we must keep the record even if some fields are invalids (e.g. required + * field is empty). + * + * @param {string} id id for a local resource + * @returns {boolean} */ - _computeOverrideDefaultFields: function (listID, position) { - var list = this.localData[listID]; - var handleField; - - // Here listID is actually just parentID, it's not yet confirmed - // to be a list. - // If we are not in the case that interests us, - // listID will be undefined and this check will work. - if (!list) { - return {}; - } - - position = position || 'bottom'; - - // Let's find if there is a field with handle. - if (!list.fieldsInfo) { - return {}; + canBeAbandoned: function (id) { + // 1. no drop if flagged + if (this.localData[id]._noAbandon) { + return false; } - for (var field in list.fieldsInfo.list) { - if (list.fieldsInfo.list[field].widget === 'handle') { - handleField = field; - break; - // If there are 2 handle fields on the same list, - // we take the first one we find. - // And that will be alphabetically on the field name... + // 2. no drop in a list on "ADD in some cases + var record = this.localData[id]; + var parent = this.localData[record.parentID]; + if (parent) { + var entry = _.findWhere(parent._savePoint, {operation: 'ADD', id: id}); + if (entry) { + // 2.1. no drop on non-new addition in list + if (!entry.isNew) { + return false; + } + // 2.2. no drop on new addition on "UPDATE" + var lastEntry = _.last(parent._savePoint); + if (lastEntry.operation === 'UPDATE' && lastEntry.id === id) { + return false; + } } } - - if (!handleField) { - return {}; - } - - // We don't want to override the default value - // if the list is not ordered by the handle field. - var isOrderedByHandle = list.orderedBy - && list.orderedBy.length - && list.orderedBy[0].asc === true - && list.orderedBy[0].name === handleField; - - if (!isOrderedByHandle) { - return {}; - } - - // We compute the list (get) to apply the pending changes before doing our work, - // otherwise new lines might not be taken into account. - // We use raw: true because we only need to load the first level of relation. - var computedList = this.get(list.id, {raw: true}); - - // We don't need to worry about the position of a new line if the list is empty. - if (!computedList || !computedList.data || !computedList.data.length) { - return {}; - } - - // If there are less elements in the list than the limit of - // the page then take the index of the last existing line. - - // If the button is at the top, we want the new element on - // the first line of the page. - - // If the button is at the bottom, we want the new element - // after the last line of the page - // (= theorically it will be the first element of the next page). - - // We ignore list.offset because computedList.data - // will only have the current page elements. - - var index = Math.min( - computedList.data.length - 1, - position !== 'top' ? list.limit - 1 : 0 - ); - - // This positioning will almost be correct. There might just be - // an issue if several other lines have the same handleFieldValue. - - // TODO ideally: if there is an element with the same handleFieldValue, - // that one and all the following elements must be incremented - // by 1 (at least until there is a gap in the numbering). - - // We don't do it now because it's not an important case. - // However, we can for sure increment by 1 if we are on the last page. - - var handleFieldValue = computedList.data[index].data[handleField]; - if (position === 'top') { - handleFieldValue--; - } else if (list.count <= list.offset + list.limit - (list.tempLimitIncrement || 0)) { - handleFieldValue++; - } - - return { - field: handleField, - value: handleFieldValue, - }; + // 3. drop new records + return this.isNew(id); }, /** * Delete a list of records, then, if the records have a parent, reload it. @@ -517,6 +482,20 @@ var BasicModel = AbstractModel.extend({ }); }); }, + /** + * For list resources, this freezes the current records order. + * + * @param {string} listID a valid element ID of type list + */ + freezeOrder: function (listID) { + var list = this.localData[listID]; + if (list.type === 'record') { + return; + } + list = this._applyX2ManyOperations(list); + this._sortList(list); + this.localData[listID].orderedResIDs = list.res_ids; + }, /** * The get method first argument is the handle returned by the load method. * It is optional (the handle can be undefined). In some case, it makes @@ -691,54 +670,6 @@ var BasicModel = AbstractModel.extend({ } return _t("New"); }, - /** - * Returns true if a record can be abandoned. - * - * Case for not abandoning the record: - * - * 1. flagged as 'no abandon' (i.e. during a `default_get`, including any - * `onchange` from a `default_get`) - * 2. registered in a list on addition - * - * 2.1. registered as non-new addition - * 2.2. registered as new additon on update - * - * 3. record is not new - * - * Otherwise, the record can be abandoned. - * - * This is useful when discarding changes on this record, as it means that - * we must keep the record even if some fields are invalids (e.g. required - * field is empty). - * - * @param {string} id id for a local resource - * @returns {boolean} - */ - canBeAbandoned: function (id) { - // 1. no drop if flagged - if (this.localData[id]._noAbandon) { - return false; - } - // 2. no drop in a list on "ADD in some cases - var record = this.localData[id]; - var parent = this.localData[record.parentID]; - if (parent) { - var entry = _.findWhere(parent._savePoint, {operation: 'ADD', id: id}); - if (entry) { - // 2.1. no drop on non-new addition in list - if (!entry.isNew) { - return false; - } - // 2.2. no drop on new addition on "UPDATE" - var lastEntry = _.last(parent._savePoint); - if (lastEntry.operation === 'UPDATE' && lastEntry.id === id) { - return false; - } - } - } - // 3. drop new records - return this.isNew(id); - }, /** * Returns true if a record is dirty. A record is considered dirty if it has * some unsaved changes, marked by the _isDirty property on the record or @@ -1175,41 +1106,6 @@ var BasicModel = AbstractModel.extend({ return def; }); }, - /** - * Completes the fields and fieldsInfo of a dataPoint with the given ones. - * It is useful for the cases where a record element is shared between - * various views, such as a one2many with a tree and a form view. - * - * @param {string} recordID a valid element ID - * @param {Object} viewInfo - * @param {Object} viewInfo.fields - * @param {Object} viewInfo.fieldsInfo - */ - addFieldsInfo: function (recordID, viewInfo) { - var record = this.localData[recordID]; - record.fields = _.extend({}, record.fields, viewInfo.fields); - // complete the given fieldsInfo with the fields of the main view, so - // that those field will be reloaded if a reload is triggered by the - // secondary view - var fieldsInfo = _.mapObject(viewInfo.fieldsInfo, function (fieldsInfo) { - return _.defaults({}, fieldsInfo, record.fieldsInfo[record.viewType]); - }); - record.fieldsInfo = _.extend({}, record.fieldsInfo, fieldsInfo); - }, - /** - * For list resources, this freezes the current records order. - * - * @param {string} listID a valid element ID of type list - */ - freezeOrder: function (listID) { - var list = this.localData[listID]; - if (list.type === 'record') { - return; - } - list = this._applyX2ManyOperations(list); - this._sortList(list); - this.localData[listID].orderedResIDs = list.res_ids; - }, /** * Manually sets a resource as dirty. This is used to notify that a field * has been modified, but with an invalid value. In that case, the value is @@ -2061,6 +1957,110 @@ var BasicModel = AbstractModel.extend({ } return hasOnchange ? specs : false; }, + /** + * Compute the default value that the handle field should take. + * We need to compute this in order for new lines to be added at the correct position. + * + * @private + * @param {Object} listID + * @param {string} position + * @return {Object} empty object if no overrie has to be done, or: + * field: the name of the field to override, + * value: the value to use for that field + */ + _computeOverrideDefaultFields: function (listID, position) { + var list = this.localData[listID]; + var handleField; + + // Here listID is actually just parentID, it's not yet confirmed + // to be a list. + // If we are not in the case that interests us, + // listID will be undefined and this check will work. + if (!list) { + return {}; + } + + position = position || 'bottom'; + + // Let's find if there is a field with handle. + if (!list.fieldsInfo) { + return {}; + } + for (var field in list.fieldsInfo.list) { + if (list.fieldsInfo.list[field].widget === 'handle') { + handleField = field; + break; + // If there are 2 handle fields on the same list, + // we take the first one we find. + // And that will be alphabetically on the field name... + } + } + + if (!handleField) { + return {}; + } + + // We don't want to override the default value + // if the list is not ordered by the handle field. + var isOrderedByHandle = list.orderedBy + && list.orderedBy.length + && list.orderedBy[0].asc === true + && list.orderedBy[0].name === handleField; + + if (!isOrderedByHandle) { + return {}; + } + + // We compute the list (get) to apply the pending changes before doing our work, + // otherwise new lines might not be taken into account. + // We use raw: true because we only need to load the first level of relation. + var computedList = this.get(list.id, {raw: true}); + + // We don't need to worry about the position of a new line if the list is empty. + if (!computedList || !computedList.data || !computedList.data.length) { + return {}; + } + + // If there are less elements in the list than the limit of + // the page then take the index of the last existing line. + + // If the button is at the top, we want the new element on + // the first line of the page. + + // If the button is at the bottom, we want the new element + // after the last line of the page + // (= theorically it will be the first element of the next page). + + // We ignore list.offset because computedList.data + // will only have the current page elements. + + var index = Math.min( + computedList.data.length - 1, + position !== 'top' ? list.limit - 1 : 0 + ); + + // This positioning will almost be correct. There might just be + // an issue if several other lines have the same handleFieldValue. + + // TODO ideally: if there is an element with the same handleFieldValue, + // that one and all the following elements must be incremented + // by 1 (at least until there is a gap in the numbering). + + // We don't do it now because it's not an important case. + // However, we can for sure increment by 1 if we are on the last page. + + var handleFieldValue = computedList.data[index].data[handleField]; + if (position === 'top') { + handleFieldValue--; + } else if (list.count <= list.offset + list.limit - (list.tempLimitIncrement || 0)) { + handleFieldValue++; + } + + return { + field: handleField, + value: handleFieldValue, + }; + }, /** * Evaluate modifiers * 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 33267283a15..8db200183ef 100644 --- a/addons/web/static/src/js/views/basic/basic_renderer.js +++ b/addons/web/static/src/js/views/basic/basic_renderer.js @@ -136,30 +136,11 @@ var BasicRenderer = AbstractRenderer.extend({ dom.setSelectionRange(field.getFocusableElement().get(0), {start: offset, end: offset}); } }, + //-------------------------------------------------------------------------- // Private //-------------------------------------------------------------------------- - /** - * Add a tooltip on a $node, depending on a field description - * - * @param {FieldWidget} widget - * @param {$node} $node - */ - _addFieldTooltip: function (widget, $node) { - // optional argument $node, the jQuery element on which the tooltip - // should be attached if not given, the tooltip is attached on the - // widget's $el - $node = $node.length ? $node : widget.$el; - $node.tooltip({ - title: function () { - return qweb.render('WidgetLabel.tooltip', { - debug: config.debug, - widget: widget, - }); - } - }); - }, /** * Activates the widget at the given index for the given record if possible * or the "next" possible one. Usually, a widget can be activated if it is @@ -238,6 +219,26 @@ var BasicRenderer = AbstractRenderer.extend({ currentIndex = currentIndex ? (currentIndex - 1) : ((this.allFieldWidgets[record.id] || []).length - 1); return this._activateFieldWidget(record, currentIndex, {inc:-1}); }, + /** + * Add a tooltip on a $node, depending on a field description + * + * @param {FieldWidget} widget + * @param {$node} $node + */ + _addFieldTooltip: function (widget, $node) { + // optional argument $node, the jQuery element on which the tooltip + // should be attached if not given, the tooltip is attached on the + // widget's $el + $node = $node.length ? $node : widget.$el; + $node.tooltip({ + title: function () { + return qweb.render('WidgetLabel.tooltip', { + debug: config.debug, + widget: widget, + }); + } + }); + }, /** * Does the necessary DOM updates to match the given modifiers data. The * modifiers data is supposed to contain the properly evaluated modifiers @@ -685,7 +686,6 @@ var BasicRenderer = AbstractRenderer.extend({ return $el; }, - /** * Rerenders a given widget and make sure the associated data which * referenced the old one is updated. -- cgit v1.2.3