From 151d57093346f7392b93bf47d23b0635455e1750 Mon Sep 17 00:00:00 2001 From: "Guillaume (gdi)" Date: Tue, 4 May 2021 14:28:44 +0000 Subject: [PATCH] [FIX] website_form: prevent input ids from being duplicated When cloning a website_form field, we rerender the cloned field to reassign its elements ids and for attributes with a unique ID instead of the old ones. Rerendering the list also removes an issue with Selection fields that were not visible after a clone and a save, as it was usually done after a _rerenderXML, not executed after a clone. Also, _renderField docstring was wrong, and its return values terminology have been replaced with correct one. task-2485093 X-original-commit: 92bacc8907fa9577805b28a11894e7a006b2a5bf Part-of: odoo/odoo#75908 --- .../src/snippets/s_website_form/options.js | 61 ++++++++++++++----- 1 file changed, 46 insertions(+), 15 deletions(-) diff --git a/addons/website/static/src/snippets/s_website_form/options.js b/addons/website/static/src/snippets/s_website_form/options.js index c7b1ecef5d6..81949c1ec78 100644 --- a/addons/website/static/src/snippets/s_website_form/options.js +++ b/addons/website/static/src/snippets/s_website_form/options.js @@ -53,6 +53,15 @@ const FormEditor = options.Class.extend({ } return field.records; }, + /** + * Generates a new ID. + * + * @private + * @returns {string} The new ID + */ + _generateUniqueID() { + return Math.random().toString(36).substring(2, 15); + }, /** * Returns a field object * @@ -120,15 +129,18 @@ const FormEditor = options.Class.extend({ /** * @private * @param {Object} field - * @returns {Promise} + * @returns {HTMLElement} */ - _renderField: function (field) { - field.id = Math.random().toString(36).substring(2, 15); // Big unique ID + _renderField: function (field, resetId = false) { + if (!field.id) { + field.id = this._generateUniqueID(); + } const template = document.createElement('template'); template.innerHTML = qweb.render("website.form_field_" + field.type, {field: field}).trim(); if (field.description && field.description !== true) { $(template.content.querySelector('.s_website_form_field_description')).replaceWith(field.description); } + template.content.querySelectorAll('input.datetimepicker-input').forEach(el => el.value = field.propertyValue); return template.content.firstElementChild; }, }); @@ -415,10 +427,10 @@ options.registry.WebsiteFormEditor = FormEditor.extend({ field.formatInfo.requiredMark = this._isRequiredMark(); field.formatInfo.optionalMark = this._isOptionalMark(); field.formatInfo.mark = this._getMark(); - const htmlField = this._renderField(field); - data.$target.after(htmlField); + const fieldEl = this._renderField(field); + data.$target.after(fieldEl); this.trigger_up('activate_snippet', { - $snippet: $(htmlField), + $snippet: $(fieldEl), }); } }, @@ -799,6 +811,17 @@ options.registry.WebsiteFieldEditor = FieldEditor.extend({ // We need to reload the existing type list. this.rerender = true; }, + /** + * Rerenders the clone to avoid id duplicates. + * + * @override + */ + onClone() { + const field = this._getActiveField(); + delete field.id; + const fieldEl = this._renderField(field); + this._replaceFieldElement(fieldEl); + }, //---------------------------------------------------------------------- // Options @@ -1013,7 +1036,7 @@ options.registry.WebsiteFieldEditor = FieldEditor.extend({ uiFragment.appendChild(list); }, /** - * Replace the target content with the field provided + * Replaces the target content with the field provided. * * @private * @param {Object} field @@ -1025,12 +1048,20 @@ options.registry.WebsiteFieldEditor = FieldEditor.extend({ if (activeField.type !== field.type) { field.value = ''; } - const htmlField = this._renderField(field); + const fieldEl = this._renderField(field); + this._replaceFieldElement(fieldEl); + }, + /** + * Replaces the target with provided field. + * + * @private + * @param {HTMLElement} fieldEl + */ + _replaceFieldElement(fieldEl) { [...this.$target[0].childNodes].forEach(node => node.remove()); - [...htmlField.childNodes].forEach(node => this.$target[0].appendChild(node)); - [...htmlField.attributes].forEach(el => this.$target[0].removeAttribute(el.nodeName)); - [...htmlField.attributes].forEach(el => this.$target[0].setAttribute(el.nodeName, el.nodeValue)); - this.$target[0].querySelectorAll('input.datetimepicker-input').forEach(el => el.value = field.propertyValue); + [...fieldEl.childNodes].forEach(node => this.$target[0].appendChild(node)); + [...fieldEl.attributes].forEach(el => this.$target[0].removeAttribute(el.nodeName)); + [...fieldEl.attributes].forEach(el => this.$target[0].setAttribute(el.nodeName, el.nodeValue)); }, /** * @private @@ -1080,10 +1111,10 @@ options.registry.AddFieldForm = FormEditor.extend({ addField: async function (previewMode, value, params) { const field = this._getCustomField('char', 'Custom Text'); field.formatInfo = this._getDefaultFormat(); - const htmlField = this._renderField(field); - this.$target.find('.s_website_form_submit, .s_website_form_recaptcha').first().before(htmlField); + const fieldEl = this._renderField(field); + this.$target.find('.s_website_form_submit, .s_website_form_recaptcha').first().before(fieldEl); this.trigger_up('activate_snippet', { - $snippet: $(htmlField), + $snippet: $(fieldEl), }); }, });