From 0d20d1e8aac17d9a37dbafbd15c3d57416f51c0c Mon Sep 17 00:00:00 2001 From: Paul Holden Date: Tue, 13 Dec 2022 08:44:08 +0000 Subject: [PATCH] MDL-76667 forms: mark modal form as submitted upon success. Ensure the form change checker module doesn't warn user about it. --- lib/form/amd/build/modalform.min.js | 2 +- lib/form/amd/build/modalform.min.js.map | 2 +- lib/form/amd/src/modalform.js | 11 ++++++----- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/form/amd/build/modalform.min.js b/lib/form/amd/build/modalform.min.js index 1177aa874ad..300b48cf234 100644 --- a/lib/form/amd/build/modalform.min.js +++ b/lib/form/amd/build/modalform.min.js @@ -1,3 +1,3 @@ -define("core_form/modalform",["exports","core/ajax","core_form/changechecker","core_form/events","core/fragment","core/modal_events","core/modal_factory","core/notification","core/pending","./util"],(function(_exports,_ajax,FormChangeChecker,FormEvents,_fragment,_modal_events,_modal_factory,_notification,_pending,_util){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_ajax=_interopRequireDefault(_ajax),FormChangeChecker=_interopRequireWildcard(FormChangeChecker),FormEvents=_interopRequireWildcard(FormEvents),_fragment=_interopRequireDefault(_fragment),_modal_events=_interopRequireDefault(_modal_events),_modal_factory=_interopRequireDefault(_modal_factory),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending);return _exports.default=class{constructor(config){var obj,key,value;value={FORM_SUBMITTED:"core_form_modalform_formsubmitted",FORM_CANCELLED:"core_form_modalform_formcancelled",CLIENT_VALIDATION_ERROR:"core_form_modalform_clientvalidationerror",SERVER_VALIDATION_ERROR:"core_form_modalform_validationerror",ERROR:"core_form_modalform_error",NOSUBMIT_BUTTON_PRESSED:"core_form_modalform_nosubmitbutton",SUBMIT_BUTTON_PRESSED:"core_form_modalform_submitbutton",CANCEL_BUTTON_PRESSED:"core_form_modalform_cancelbutton",LOADED:"core_form_modalform_loaded"},(key="events")in(obj=this)?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,this.modal=null,this.config=config,this.config.modalConfig={removeOnClose:!0,type:_modal_factory.default.types.SAVE_CANCEL,large:!0,...this.config.modalConfig||{}},this.config.args=this.config.args||{},this.futureListeners=[]}show(){const pendingPromise=new _pending.default("core_form/modalform:init");return _modal_factory.default.create(this.config.modalConfig).then((modal=>{this.modal=modal;const formParams=(0,_util.serialize)(this.config.args||{}),bodyContent=this.getBody(formParams);return this.modal.setBodyContent(bodyContent),bodyContent.catch(_notification.default.exception),this.modal.getRoot().on(_modal_events.default.hidden,(()=>{this.notifyResetFormChanges(),this.modal.destroy(),this.config.returnFocus&&this.config.returnFocus.focus()})),this.modal.getModal().addClass("modal-form-dialogue"),this.modal.getRoot().on("click","form input[type=submit][data-no-submit]",(e=>{e.preventDefault();this.trigger(this.events.NOSUBMIT_BUTTON_PRESSED,e.target).defaultPrevented||this.processNoSubmitButton(e.target)})),this.modal.getRoot().on("submit","form",(e=>{e.preventDefault();this.trigger(this.events.SUBMIT_BUTTON_PRESSED).defaultPrevented||this.submitFormAjax()})),void 0!==this.config.saveButtonText&&void 0!==this.modal.setSaveButtonText&&this.modal.setSaveButtonText(this.config.saveButtonText),void 0!==this.config.saveButtonClasses&&this.setSaveButtonClasses(this.config.saveButtonClasses),this.modal.getRoot().on(_modal_events.default.save,(e=>{e.preventDefault(),this.modal.getRoot().find("form").submit()})),this.modal.getRoot().on(_modal_events.default.cancel,(e=>{this.trigger(this.events.CANCEL_BUTTON_PRESSED).defaultPrevented&&e.preventDefault()})),this.futureListeners.forEach((args=>this.modal.getRoot()[0].addEventListener(...args))),this.futureListeners=[],this.trigger(this.events.LOADED,null,!1),this.modal.show()})).then(pendingPromise.resolve)}trigger(eventName){const e=new CustomEvent(eventName,{detail:arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,cancelable:!(arguments.length>2&&void 0!==arguments[2])||arguments[2]});return this.modal.getRoot()[0].dispatchEvent(e),e}addEventListener(){for(var _len=arguments.length,args=new Array(_len),_key=0;_key<_len;_key++)args[_key]=arguments[_key];this.modal?this.modal.getRoot()[0].addEventListener(...args):this.futureListeners.push(args)}getBody(formDataString){const params={formdata:formDataString,form:this.config.formClass},pendingPromise=new _pending.default("core_form/modalform:form_body");return _ajax.default.call([{methodname:"core_form_dynamic_form",args:params}])[0].then((response=>(pendingPromise.resolve(),{html:response.html,js:_fragment.default.processCollectedJavascript(response.javascript)})))}onSubmitError(exception){this.trigger(this.events.ERROR,exception).defaultPrevented||_notification.default.exception(exception)}notifyResetFormChanges(){const form=this.getFormNode();FormEvents.notifyFormSubmittedByJavascript(this.getFormNode(),!0),form&&FormChangeChecker.resetFormDirtyState(this.getFormNode())}getFormNode(){return this.modal.getRoot().find("form")[0]}processNoSubmitButton(button){const form=this.getFormNode();if(!form)return;FormEvents.notifyFormSubmittedByJavascript(form,!0);let formData=this.modal.getRoot().find("form").serialize();formData=formData+"&"+encodeURIComponent(button.getAttribute("name"))+"="+encodeURIComponent(button.getAttribute("value"));const bodyContent=this.getBody(formData);this.modal.setBodyContent(bodyContent),bodyContent.catch(_notification.default.exception)}validateElements(){FormEvents.notifyFormSubmittedByJavascript(this.getFormNode());const invalid=this.modal.getRoot().find('[aria-invalid="true"], .error');return!invalid.length||(invalid.first().focus(),!1)}disableButtons(){this.modal.getFooter().find("[data-action]").attr("disabled",!0)}enableButtons(){this.modal.getFooter().find("[data-action]").removeAttr("disabled")}async submitFormAjax(){if(!this.validateElements())return void this.trigger(this.events.CLIENT_VALIDATION_ERROR,null,!1);this.disableButtons();const formData=this.modal.getRoot().find("form").serialize();_ajax.default.call([{methodname:"core_form_dynamic_form",args:{formdata:formData,form:this.config.formClass}}])[0].then((response=>{if(response.submitted){const data=JSON.parse(response.data);this.notifyResetFormChanges();this.trigger(this.events.FORM_SUBMITTED,data).defaultPrevented||this.modal.hide()}else{const promise=new Promise((resolve=>resolve({html:response.html,js:_fragment.default.processCollectedJavascript(response.javascript)})));this.modal.setBodyContent(promise),this.enableButtons(),this.trigger(this.events.SERVER_VALIDATION_ERROR)}return null})).catch((exception=>this.onSubmitError(exception)))}setSaveButtonClasses(value){const button=this.modal.getFooter().find("[data-action='save']");if(!button)throw new Error("Unable to find the 'save' button");button.removeClass().addClass(value)}},_exports.default})); +define("core_form/modalform",["exports","core/ajax","core_form/changechecker","core_form/events","core/fragment","core/modal_events","core/modal_factory","core/notification","core/pending","./util"],(function(_exports,_ajax,FormChangeChecker,FormEvents,_fragment,_modal_events,_modal_factory,_notification,_pending,_util){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_ajax=_interopRequireDefault(_ajax),FormChangeChecker=_interopRequireWildcard(FormChangeChecker),FormEvents=_interopRequireWildcard(FormEvents),_fragment=_interopRequireDefault(_fragment),_modal_events=_interopRequireDefault(_modal_events),_modal_factory=_interopRequireDefault(_modal_factory),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending);return _exports.default=class{constructor(config){var obj,key,value;value={FORM_SUBMITTED:"core_form_modalform_formsubmitted",FORM_CANCELLED:"core_form_modalform_formcancelled",CLIENT_VALIDATION_ERROR:"core_form_modalform_clientvalidationerror",SERVER_VALIDATION_ERROR:"core_form_modalform_validationerror",ERROR:"core_form_modalform_error",NOSUBMIT_BUTTON_PRESSED:"core_form_modalform_nosubmitbutton",SUBMIT_BUTTON_PRESSED:"core_form_modalform_submitbutton",CANCEL_BUTTON_PRESSED:"core_form_modalform_cancelbutton",LOADED:"core_form_modalform_loaded"},(key="events")in(obj=this)?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,this.modal=null,this.config=config,this.config.modalConfig={removeOnClose:!0,type:_modal_factory.default.types.SAVE_CANCEL,large:!0,...this.config.modalConfig||{}},this.config.args=this.config.args||{},this.futureListeners=[]}show(){const pendingPromise=new _pending.default("core_form/modalform:init");return _modal_factory.default.create(this.config.modalConfig).then((modal=>{this.modal=modal;const formParams=(0,_util.serialize)(this.config.args||{}),bodyContent=this.getBody(formParams);return this.modal.setBodyContent(bodyContent),bodyContent.catch(_notification.default.exception),this.modal.getRoot().on(_modal_events.default.hidden,(()=>{this.notifyResetFormChanges(),this.modal.destroy(),this.config.returnFocus&&this.config.returnFocus.focus()})),this.modal.getModal().addClass("modal-form-dialogue"),this.modal.getRoot().on("click","form input[type=submit][data-no-submit]",(e=>{e.preventDefault();this.trigger(this.events.NOSUBMIT_BUTTON_PRESSED,e.target).defaultPrevented||this.processNoSubmitButton(e.target)})),this.modal.getRoot().on("submit","form",(e=>{e.preventDefault();this.trigger(this.events.SUBMIT_BUTTON_PRESSED).defaultPrevented||this.submitFormAjax()})),void 0!==this.config.saveButtonText&&void 0!==this.modal.setSaveButtonText&&this.modal.setSaveButtonText(this.config.saveButtonText),void 0!==this.config.saveButtonClasses&&this.setSaveButtonClasses(this.config.saveButtonClasses),this.modal.getRoot().on(_modal_events.default.save,(e=>{e.preventDefault(),this.modal.getRoot().find("form").submit()})),this.modal.getRoot().on(_modal_events.default.cancel,(e=>{this.trigger(this.events.CANCEL_BUTTON_PRESSED).defaultPrevented&&e.preventDefault()})),this.futureListeners.forEach((args=>this.modal.getRoot()[0].addEventListener(...args))),this.futureListeners=[],this.trigger(this.events.LOADED,null,!1),this.modal.show()})).then(pendingPromise.resolve)}trigger(eventName){const e=new CustomEvent(eventName,{detail:arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,cancelable:!(arguments.length>2&&void 0!==arguments[2])||arguments[2]});return this.modal.getRoot()[0].dispatchEvent(e),e}addEventListener(){for(var _len=arguments.length,args=new Array(_len),_key=0;_key<_len;_key++)args[_key]=arguments[_key];this.modal?this.modal.getRoot()[0].addEventListener(...args):this.futureListeners.push(args)}getBody(formDataString){const params={formdata:formDataString,form:this.config.formClass},pendingPromise=new _pending.default("core_form/modalform:form_body");return _ajax.default.call([{methodname:"core_form_dynamic_form",args:params}])[0].then((response=>(pendingPromise.resolve(),{html:response.html,js:_fragment.default.processCollectedJavascript(response.javascript)})))}onSubmitError(exception){this.trigger(this.events.ERROR,exception).defaultPrevented||_notification.default.exception(exception)}notifyResetFormChanges(){const form=this.getFormNode();form&&(FormEvents.notifyFormSubmittedByJavascript(form,!0),FormChangeChecker.resetFormDirtyState(form))}getFormNode(){return this.modal.getRoot().find("form")[0]}processNoSubmitButton(button){const form=this.getFormNode();if(!form)return;FormEvents.notifyFormSubmittedByJavascript(form,!0);let formData=this.modal.getRoot().find("form").serialize();formData=formData+"&"+encodeURIComponent(button.getAttribute("name"))+"="+encodeURIComponent(button.getAttribute("value"));const bodyContent=this.getBody(formData);this.modal.setBodyContent(bodyContent),bodyContent.catch(_notification.default.exception)}validateElements(){FormEvents.notifyFormSubmittedByJavascript(this.getFormNode());const invalid=this.modal.getRoot().find('[aria-invalid="true"], .error');return!invalid.length||(invalid.first().focus(),!1)}disableButtons(){this.modal.getFooter().find("[data-action]").attr("disabled",!0)}enableButtons(){this.modal.getFooter().find("[data-action]").removeAttr("disabled")}async submitFormAjax(){if(!this.validateElements())return void this.trigger(this.events.CLIENT_VALIDATION_ERROR,null,!1);this.disableButtons();const form=this.modal.getRoot().find("form"),formData=form.serialize();_ajax.default.call([{methodname:"core_form_dynamic_form",args:{formdata:formData,form:this.config.formClass}}])[0].then((response=>{if(response.submitted){const data=JSON.parse(response.data);FormChangeChecker.markFormSubmitted(form[0]);this.trigger(this.events.FORM_SUBMITTED,data).defaultPrevented||this.modal.hide()}else{const promise=new Promise((resolve=>resolve({html:response.html,js:_fragment.default.processCollectedJavascript(response.javascript)})));this.modal.setBodyContent(promise),this.enableButtons(),this.trigger(this.events.SERVER_VALIDATION_ERROR)}return null})).catch((exception=>this.onSubmitError(exception)))}setSaveButtonClasses(value){const button=this.modal.getFooter().find("[data-action='save']");if(!button)throw new Error("Unable to find the 'save' button");button.removeClass().addClass(value)}},_exports.default})); //# sourceMappingURL=modalform.min.js.map \ No newline at end of file diff --git a/lib/form/amd/build/modalform.min.js.map b/lib/form/amd/build/modalform.min.js.map index 90552c8c029..4b61a853d64 100644 --- a/lib/form/amd/build/modalform.min.js.map +++ b/lib/form/amd/build/modalform.min.js.map @@ -1 +1 @@ -{"version":3,"file":"modalform.min.js","sources":["../src/modalform.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Display a form in a modal dialogue\n *\n * Example:\n * import ModalForm from 'core_form/modalform';\n *\n * const modalForm = new ModalForm({\n * formClass: 'pluginname\\\\form\\\\formname',\n * modalConfig: {title: 'Here comes the title'},\n * args: {categoryid: 123},\n * returnFocus: e.target,\n * });\n * modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, (c) => window.console.log(c.detail));\n * modalForm.show();\n *\n * See also https://docs.moodle.org/dev/Modal_and_AJAX_forms\n *\n * @module core_form/modalform\n * @copyright 2018 Mitxel Moriana \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Ajax from 'core/ajax';\nimport * as FormChangeChecker from 'core_form/changechecker';\nimport * as FormEvents from 'core_form/events';\nimport Fragment from 'core/fragment';\nimport ModalEvents from 'core/modal_events';\nimport ModalFactory from 'core/modal_factory';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {serialize} from './util';\n\nexport default class ModalForm {\n\n /**\n * Various events that can be observed.\n *\n * @type {Object}\n */\n events = {\n // Form was successfully submitted - the response is passed to the event listener.\n // Cancellable (but it's hardly ever needed to cancel this event).\n FORM_SUBMITTED: 'core_form_modalform_formsubmitted',\n // Cancel button was pressed.\n // Cancellable (but it's hardly ever needed to cancel this event).\n FORM_CANCELLED: 'core_form_modalform_formcancelled',\n // User attempted to submit the form but there was client-side validation error.\n CLIENT_VALIDATION_ERROR: 'core_form_modalform_clientvalidationerror',\n // User attempted to submit the form but server returned validation error.\n SERVER_VALIDATION_ERROR: 'core_form_modalform_validationerror',\n // Error occurred while performing request to the server.\n // Cancellable (by default calls Notification.exception).\n ERROR: 'core_form_modalform_error',\n // Right after user pressed no-submit button,\n // listen to this event if you want to add JS validation or processing for no-submit button.\n // Cancellable.\n NOSUBMIT_BUTTON_PRESSED: 'core_form_modalform_nosubmitbutton',\n // Right after user pressed submit button,\n // listen to this event if you want to add additional JS validation or confirmation dialog.\n // Cancellable.\n SUBMIT_BUTTON_PRESSED: 'core_form_modalform_submitbutton',\n // Right after user pressed cancel button,\n // listen to this event if you want to add confirmation dialog.\n // Cancellable.\n CANCEL_BUTTON_PRESSED: 'core_form_modalform_cancelbutton',\n // Modal was loaded and this.modal is available (but the form content may not be loaded yet).\n LOADED: 'core_form_modalform_loaded',\n };\n\n /**\n * Constructor\n *\n * Shows the required form inside a modal dialogue\n *\n * @param {Object} config parameters for the form and modal dialogue:\n * @paramy {String} config.formClass PHP class name that handles the form (should extend \\core_form\\modal )\n * @paramy {Object} config.modalConfig modal config - title, type, etc.\n * Default: {removeOnClose: true, type: ModalFactory.types.SAVE_CANCEL}\n * @paramy {Object} config.args Arguments for the initial form rendering (for example, id of the edited entity)\n * @paramy {String} config.saveButtonText the text to display on the Modal \"Save\" button (optional)\n * @paramy {String} config.saveButtonClasses additional CSS classes for the Modal \"Save\" button\n * @paramy {HTMLElement} config.returnFocus element to return focus to after the dialogue is closed\n */\n constructor(config) {\n this.modal = null;\n this.config = config;\n this.config.modalConfig = {\n removeOnClose: true,\n type: ModalFactory.types.SAVE_CANCEL,\n large: true,\n ...(this.config.modalConfig || {}),\n };\n this.config.args = this.config.args || {};\n this.futureListeners = [];\n }\n\n /**\n * Initialise the modal and shows it\n *\n * @return {Promise}\n */\n show() {\n const pendingPromise = new Pending('core_form/modalform:init');\n\n return ModalFactory.create(this.config.modalConfig)\n .then((modal) => {\n this.modal = modal;\n\n // Retrieve the form and set the modal body. We can not set the body in the modalConfig,\n // we need to make sure that the modal already exists when we render the form. Some form elements\n // such as date_selector inspect the existing elements on the page to find the highest z-index.\n const formParams = serialize(this.config.args || {});\n const bodyContent = this.getBody(formParams);\n this.modal.setBodyContent(bodyContent);\n bodyContent.catch(Notification.exception);\n\n // After successfull submit, when we press \"Cancel\" or close the dialogue by clicking on X in the top right corner.\n this.modal.getRoot().on(ModalEvents.hidden, () => {\n this.notifyResetFormChanges();\n this.modal.destroy();\n // Focus on the element that actually launched the modal.\n if (this.config.returnFocus) {\n this.config.returnFocus.focus();\n }\n });\n\n // Add the class to the modal dialogue.\n this.modal.getModal().addClass('modal-form-dialogue');\n\n // We catch the press on submit buttons in the forms.\n this.modal.getRoot().on('click', 'form input[type=submit][data-no-submit]',\n (e) => {\n e.preventDefault();\n const event = this.trigger(this.events.NOSUBMIT_BUTTON_PRESSED, e.target);\n if (!event.defaultPrevented) {\n this.processNoSubmitButton(e.target);\n }\n });\n\n // We catch the form submit event and use it to submit the form with ajax.\n this.modal.getRoot().on('submit', 'form', (e) => {\n e.preventDefault();\n const event = this.trigger(this.events.SUBMIT_BUTTON_PRESSED);\n if (!event.defaultPrevented) {\n this.submitFormAjax();\n }\n });\n\n // Change the text for the save button.\n if (typeof this.config.saveButtonText !== 'undefined' &&\n typeof this.modal.setSaveButtonText !== 'undefined') {\n this.modal.setSaveButtonText(this.config.saveButtonText);\n }\n // Set classes for the save button.\n if (typeof this.config.saveButtonClasses !== 'undefined') {\n this.setSaveButtonClasses(this.config.saveButtonClasses);\n }\n // When Save button is pressed - submit the form.\n this.modal.getRoot().on(ModalEvents.save, (e) => {\n e.preventDefault();\n this.modal.getRoot().find('form').submit();\n });\n\n // When Cancel button is pressed - allow to intercept.\n this.modal.getRoot().on(ModalEvents.cancel, (e) => {\n const event = this.trigger(this.events.CANCEL_BUTTON_PRESSED);\n if (event.defaultPrevented) {\n e.preventDefault();\n }\n });\n this.futureListeners.forEach(args => this.modal.getRoot()[0].addEventListener(...args));\n this.futureListeners = [];\n this.trigger(this.events.LOADED, null, false);\n return this.modal.show();\n })\n .then(pendingPromise.resolve);\n }\n\n /**\n * Triggers a custom event\n *\n * @private\n * @param {String} eventName\n * @param {*} detail\n * @param {Boolean} cancelable\n * @return {CustomEvent}\n */\n trigger(eventName, detail = null, cancelable = true) {\n const e = new CustomEvent(eventName, {detail, cancelable});\n this.modal.getRoot()[0].dispatchEvent(e);\n return e;\n }\n\n /**\n * Add listener for an event\n *\n * @param {array} args\n * @example:\n * const modalForm = new ModalForm(...);\n * dynamicForm.addEventListener(modalForm.events.FORM_SUBMITTED, e => {\n * window.console.log(e.detail);\n * });\n */\n addEventListener(...args) {\n if (!this.modal) {\n this.futureListeners.push(args);\n } else {\n this.modal.getRoot()[0].addEventListener(...args);\n }\n }\n\n /**\n * Get form contents (to be used in ModalForm.setBodyContent())\n *\n * @param {String} formDataString form data in format of a query string\n * @method getBody\n * @private\n * @return {Promise}\n */\n getBody(formDataString) {\n const params = {\n formdata: formDataString,\n form: this.config.formClass\n };\n const pendingPromise = new Pending('core_form/modalform:form_body');\n return Ajax.call([{\n methodname: 'core_form_dynamic_form',\n args: params\n }])[0]\n .then(response => {\n pendingPromise.resolve();\n return {html: response.html, js: Fragment.processCollectedJavascript(response.javascript)};\n });\n }\n\n /**\n * On exception during form processing. Caller may override\n *\n * @param {Object} exception\n */\n onSubmitError(exception) {\n const event = this.trigger(this.events.ERROR, exception);\n if (event.defaultPrevented) {\n return;\n }\n\n Notification.exception(exception);\n }\n\n /**\n * Notifies listeners that form dirty state should be reset.\n *\n * @fires event:formSubmittedByJavascript\n */\n notifyResetFormChanges() {\n const form = this.getFormNode();\n FormEvents.notifyFormSubmittedByJavascript(this.getFormNode(), true);\n\n if (!form) {\n return;\n }\n\n FormChangeChecker.resetFormDirtyState(this.getFormNode());\n }\n\n /**\n * Get the form node from the Dialogue.\n *\n * @returns {HTMLFormElement}\n */\n getFormNode() {\n return this.modal.getRoot().find('form')[0];\n }\n\n /**\n * Click on a \"submit\" button that is marked in the form as registerNoSubmitButton()\n *\n * @param {Element} button button that was pressed\n * @fires event:formSubmittedByJavascript\n */\n processNoSubmitButton(button) {\n const form = this.getFormNode();\n if (!form) {\n return;\n }\n\n FormEvents.notifyFormSubmittedByJavascript(form, true);\n\n // Add the button name to the form data and submit it.\n let formData = this.modal.getRoot().find('form').serialize();\n formData = formData + '&' + encodeURIComponent(button.getAttribute('name')) + '=' +\n encodeURIComponent(button.getAttribute('value'));\n\n const bodyContent = this.getBody(formData);\n this.modal.setBodyContent(bodyContent);\n bodyContent.catch(Notification.exception);\n }\n\n /**\n * Validate form elements\n * @return {Boolean} Whether client-side validation has passed, false if there are errors\n * @fires event:formSubmittedByJavascript\n */\n validateElements() {\n FormEvents.notifyFormSubmittedByJavascript(this.getFormNode());\n\n // Now the change events have run, see if there are any \"invalid\" form fields.\n /** @var {jQuery} list of elements with errors */\n const invalid = this.modal.getRoot().find('[aria-invalid=\"true\"], .error');\n\n // If we found invalid fields, focus on the first one and do not submit via ajax.\n if (invalid.length) {\n invalid.first().focus();\n return false;\n }\n\n return true;\n }\n\n /**\n * Disable buttons during form submission\n */\n disableButtons() {\n this.modal.getFooter().find('[data-action]').attr('disabled', true);\n }\n\n /**\n * Enable buttons after form submission (on validation error)\n */\n enableButtons() {\n this.modal.getFooter().find('[data-action]').removeAttr('disabled');\n }\n\n /**\n * Submit the form via AJAX call to the core_form_dynamic_form WS\n */\n async submitFormAjax() {\n // If we found invalid fields, focus on the first one and do not submit via ajax.\n if (!this.validateElements()) {\n this.trigger(this.events.CLIENT_VALIDATION_ERROR, null, false);\n return;\n }\n this.disableButtons();\n\n // Convert all the form elements values to a serialised string.\n const formData = this.modal.getRoot().find('form').serialize();\n\n // Now we can continue...\n Ajax.call([{\n methodname: 'core_form_dynamic_form',\n args: {\n formdata: formData,\n form: this.config.formClass\n }\n }])[0]\n .then((response) => {\n if (!response.submitted) {\n // Form was not submitted because validation failed.\n const promise = new Promise(\n resolve => resolve({html: response.html, js: Fragment.processCollectedJavascript(response.javascript)}));\n this.modal.setBodyContent(promise);\n this.enableButtons();\n this.trigger(this.events.SERVER_VALIDATION_ERROR);\n } else {\n // Form was submitted properly. Hide the modal and execute callback.\n const data = JSON.parse(response.data);\n this.notifyResetFormChanges();\n const event = this.trigger(this.events.FORM_SUBMITTED, data);\n if (!event.defaultPrevented) {\n this.modal.hide();\n }\n }\n return null;\n })\n .catch(exception => this.onSubmitError(exception));\n }\n\n /**\n * Set the classes for the 'save' button.\n *\n * @method setSaveButtonClasses\n * @param {(String)} value The 'save' button classes.\n */\n setSaveButtonClasses(value) {\n const button = this.modal.getFooter().find(\"[data-action='save']\");\n if (!button) {\n throw new Error(\"Unable to find the 'save' button\");\n }\n button.removeClass().addClass(value);\n }\n}\n"],"names":["constructor","config","FORM_SUBMITTED","FORM_CANCELLED","CLIENT_VALIDATION_ERROR","SERVER_VALIDATION_ERROR","ERROR","NOSUBMIT_BUTTON_PRESSED","SUBMIT_BUTTON_PRESSED","CANCEL_BUTTON_PRESSED","LOADED","modal","modalConfig","removeOnClose","type","ModalFactory","types","SAVE_CANCEL","large","this","args","futureListeners","show","pendingPromise","Pending","create","then","formParams","bodyContent","getBody","setBodyContent","catch","Notification","exception","getRoot","on","ModalEvents","hidden","notifyResetFormChanges","destroy","returnFocus","focus","getModal","addClass","e","preventDefault","trigger","events","target","defaultPrevented","processNoSubmitButton","submitFormAjax","saveButtonText","setSaveButtonText","saveButtonClasses","setSaveButtonClasses","save","find","submit","cancel","forEach","addEventListener","resolve","eventName","CustomEvent","detail","cancelable","dispatchEvent","push","formDataString","params","formdata","form","formClass","Ajax","call","methodname","response","html","js","Fragment","processCollectedJavascript","javascript","onSubmitError","getFormNode","FormEvents","notifyFormSubmittedByJavascript","FormChangeChecker","resetFormDirtyState","button","formData","serialize","encodeURIComponent","getAttribute","validateElements","invalid","length","first","disableButtons","getFooter","attr","enableButtons","removeAttr","submitted","data","JSON","parse","hide","promise","Promise","value","Error","removeClass"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"modalform.min.js","sources":["../src/modalform.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Display a form in a modal dialogue\n *\n * Example:\n * import ModalForm from 'core_form/modalform';\n *\n * const modalForm = new ModalForm({\n * formClass: 'pluginname\\\\form\\\\formname',\n * modalConfig: {title: 'Here comes the title'},\n * args: {categoryid: 123},\n * returnFocus: e.target,\n * });\n * modalForm.addEventListener(modalForm.events.FORM_SUBMITTED, (c) => window.console.log(c.detail));\n * modalForm.show();\n *\n * See also https://docs.moodle.org/dev/Modal_and_AJAX_forms\n *\n * @module core_form/modalform\n * @copyright 2018 Mitxel Moriana \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Ajax from 'core/ajax';\nimport * as FormChangeChecker from 'core_form/changechecker';\nimport * as FormEvents from 'core_form/events';\nimport Fragment from 'core/fragment';\nimport ModalEvents from 'core/modal_events';\nimport ModalFactory from 'core/modal_factory';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {serialize} from './util';\n\nexport default class ModalForm {\n\n /**\n * Various events that can be observed.\n *\n * @type {Object}\n */\n events = {\n // Form was successfully submitted - the response is passed to the event listener.\n // Cancellable (but it's hardly ever needed to cancel this event).\n FORM_SUBMITTED: 'core_form_modalform_formsubmitted',\n // Cancel button was pressed.\n // Cancellable (but it's hardly ever needed to cancel this event).\n FORM_CANCELLED: 'core_form_modalform_formcancelled',\n // User attempted to submit the form but there was client-side validation error.\n CLIENT_VALIDATION_ERROR: 'core_form_modalform_clientvalidationerror',\n // User attempted to submit the form but server returned validation error.\n SERVER_VALIDATION_ERROR: 'core_form_modalform_validationerror',\n // Error occurred while performing request to the server.\n // Cancellable (by default calls Notification.exception).\n ERROR: 'core_form_modalform_error',\n // Right after user pressed no-submit button,\n // listen to this event if you want to add JS validation or processing for no-submit button.\n // Cancellable.\n NOSUBMIT_BUTTON_PRESSED: 'core_form_modalform_nosubmitbutton',\n // Right after user pressed submit button,\n // listen to this event if you want to add additional JS validation or confirmation dialog.\n // Cancellable.\n SUBMIT_BUTTON_PRESSED: 'core_form_modalform_submitbutton',\n // Right after user pressed cancel button,\n // listen to this event if you want to add confirmation dialog.\n // Cancellable.\n CANCEL_BUTTON_PRESSED: 'core_form_modalform_cancelbutton',\n // Modal was loaded and this.modal is available (but the form content may not be loaded yet).\n LOADED: 'core_form_modalform_loaded',\n };\n\n /**\n * Constructor\n *\n * Shows the required form inside a modal dialogue\n *\n * @param {Object} config parameters for the form and modal dialogue:\n * @paramy {String} config.formClass PHP class name that handles the form (should extend \\core_form\\modal )\n * @paramy {Object} config.modalConfig modal config - title, type, etc.\n * Default: {removeOnClose: true, type: ModalFactory.types.SAVE_CANCEL}\n * @paramy {Object} config.args Arguments for the initial form rendering (for example, id of the edited entity)\n * @paramy {String} config.saveButtonText the text to display on the Modal \"Save\" button (optional)\n * @paramy {String} config.saveButtonClasses additional CSS classes for the Modal \"Save\" button\n * @paramy {HTMLElement} config.returnFocus element to return focus to after the dialogue is closed\n */\n constructor(config) {\n this.modal = null;\n this.config = config;\n this.config.modalConfig = {\n removeOnClose: true,\n type: ModalFactory.types.SAVE_CANCEL,\n large: true,\n ...(this.config.modalConfig || {}),\n };\n this.config.args = this.config.args || {};\n this.futureListeners = [];\n }\n\n /**\n * Initialise the modal and shows it\n *\n * @return {Promise}\n */\n show() {\n const pendingPromise = new Pending('core_form/modalform:init');\n\n return ModalFactory.create(this.config.modalConfig)\n .then((modal) => {\n this.modal = modal;\n\n // Retrieve the form and set the modal body. We can not set the body in the modalConfig,\n // we need to make sure that the modal already exists when we render the form. Some form elements\n // such as date_selector inspect the existing elements on the page to find the highest z-index.\n const formParams = serialize(this.config.args || {});\n const bodyContent = this.getBody(formParams);\n this.modal.setBodyContent(bodyContent);\n bodyContent.catch(Notification.exception);\n\n // After successfull submit, when we press \"Cancel\" or close the dialogue by clicking on X in the top right corner.\n this.modal.getRoot().on(ModalEvents.hidden, () => {\n this.notifyResetFormChanges();\n this.modal.destroy();\n // Focus on the element that actually launched the modal.\n if (this.config.returnFocus) {\n this.config.returnFocus.focus();\n }\n });\n\n // Add the class to the modal dialogue.\n this.modal.getModal().addClass('modal-form-dialogue');\n\n // We catch the press on submit buttons in the forms.\n this.modal.getRoot().on('click', 'form input[type=submit][data-no-submit]',\n (e) => {\n e.preventDefault();\n const event = this.trigger(this.events.NOSUBMIT_BUTTON_PRESSED, e.target);\n if (!event.defaultPrevented) {\n this.processNoSubmitButton(e.target);\n }\n });\n\n // We catch the form submit event and use it to submit the form with ajax.\n this.modal.getRoot().on('submit', 'form', (e) => {\n e.preventDefault();\n const event = this.trigger(this.events.SUBMIT_BUTTON_PRESSED);\n if (!event.defaultPrevented) {\n this.submitFormAjax();\n }\n });\n\n // Change the text for the save button.\n if (typeof this.config.saveButtonText !== 'undefined' &&\n typeof this.modal.setSaveButtonText !== 'undefined') {\n this.modal.setSaveButtonText(this.config.saveButtonText);\n }\n // Set classes for the save button.\n if (typeof this.config.saveButtonClasses !== 'undefined') {\n this.setSaveButtonClasses(this.config.saveButtonClasses);\n }\n // When Save button is pressed - submit the form.\n this.modal.getRoot().on(ModalEvents.save, (e) => {\n e.preventDefault();\n this.modal.getRoot().find('form').submit();\n });\n\n // When Cancel button is pressed - allow to intercept.\n this.modal.getRoot().on(ModalEvents.cancel, (e) => {\n const event = this.trigger(this.events.CANCEL_BUTTON_PRESSED);\n if (event.defaultPrevented) {\n e.preventDefault();\n }\n });\n this.futureListeners.forEach(args => this.modal.getRoot()[0].addEventListener(...args));\n this.futureListeners = [];\n this.trigger(this.events.LOADED, null, false);\n return this.modal.show();\n })\n .then(pendingPromise.resolve);\n }\n\n /**\n * Triggers a custom event\n *\n * @private\n * @param {String} eventName\n * @param {*} detail\n * @param {Boolean} cancelable\n * @return {CustomEvent}\n */\n trigger(eventName, detail = null, cancelable = true) {\n const e = new CustomEvent(eventName, {detail, cancelable});\n this.modal.getRoot()[0].dispatchEvent(e);\n return e;\n }\n\n /**\n * Add listener for an event\n *\n * @param {array} args\n * @example:\n * const modalForm = new ModalForm(...);\n * dynamicForm.addEventListener(modalForm.events.FORM_SUBMITTED, e => {\n * window.console.log(e.detail);\n * });\n */\n addEventListener(...args) {\n if (!this.modal) {\n this.futureListeners.push(args);\n } else {\n this.modal.getRoot()[0].addEventListener(...args);\n }\n }\n\n /**\n * Get form contents (to be used in ModalForm.setBodyContent())\n *\n * @param {String} formDataString form data in format of a query string\n * @method getBody\n * @private\n * @return {Promise}\n */\n getBody(formDataString) {\n const params = {\n formdata: formDataString,\n form: this.config.formClass\n };\n const pendingPromise = new Pending('core_form/modalform:form_body');\n return Ajax.call([{\n methodname: 'core_form_dynamic_form',\n args: params\n }])[0]\n .then(response => {\n pendingPromise.resolve();\n return {html: response.html, js: Fragment.processCollectedJavascript(response.javascript)};\n });\n }\n\n /**\n * On exception during form processing. Caller may override\n *\n * @param {Object} exception\n */\n onSubmitError(exception) {\n const event = this.trigger(this.events.ERROR, exception);\n if (event.defaultPrevented) {\n return;\n }\n\n Notification.exception(exception);\n }\n\n /**\n * Notifies listeners that form dirty state should be reset.\n *\n * @fires event:formSubmittedByJavascript\n */\n notifyResetFormChanges() {\n const form = this.getFormNode();\n if (!form) {\n return;\n }\n\n FormEvents.notifyFormSubmittedByJavascript(form, true);\n\n FormChangeChecker.resetFormDirtyState(form);\n }\n\n /**\n * Get the form node from the Dialogue.\n *\n * @returns {HTMLFormElement}\n */\n getFormNode() {\n return this.modal.getRoot().find('form')[0];\n }\n\n /**\n * Click on a \"submit\" button that is marked in the form as registerNoSubmitButton()\n *\n * @param {Element} button button that was pressed\n * @fires event:formSubmittedByJavascript\n */\n processNoSubmitButton(button) {\n const form = this.getFormNode();\n if (!form) {\n return;\n }\n\n FormEvents.notifyFormSubmittedByJavascript(form, true);\n\n // Add the button name to the form data and submit it.\n let formData = this.modal.getRoot().find('form').serialize();\n formData = formData + '&' + encodeURIComponent(button.getAttribute('name')) + '=' +\n encodeURIComponent(button.getAttribute('value'));\n\n const bodyContent = this.getBody(formData);\n this.modal.setBodyContent(bodyContent);\n bodyContent.catch(Notification.exception);\n }\n\n /**\n * Validate form elements\n * @return {Boolean} Whether client-side validation has passed, false if there are errors\n * @fires event:formSubmittedByJavascript\n */\n validateElements() {\n FormEvents.notifyFormSubmittedByJavascript(this.getFormNode());\n\n // Now the change events have run, see if there are any \"invalid\" form fields.\n /** @var {jQuery} list of elements with errors */\n const invalid = this.modal.getRoot().find('[aria-invalid=\"true\"], .error');\n\n // If we found invalid fields, focus on the first one and do not submit via ajax.\n if (invalid.length) {\n invalid.first().focus();\n return false;\n }\n\n return true;\n }\n\n /**\n * Disable buttons during form submission\n */\n disableButtons() {\n this.modal.getFooter().find('[data-action]').attr('disabled', true);\n }\n\n /**\n * Enable buttons after form submission (on validation error)\n */\n enableButtons() {\n this.modal.getFooter().find('[data-action]').removeAttr('disabled');\n }\n\n /**\n * Submit the form via AJAX call to the core_form_dynamic_form WS\n */\n async submitFormAjax() {\n // If we found invalid fields, focus on the first one and do not submit via ajax.\n if (!this.validateElements()) {\n this.trigger(this.events.CLIENT_VALIDATION_ERROR, null, false);\n return;\n }\n this.disableButtons();\n\n // Convert all the form elements values to a serialised string.\n const form = this.modal.getRoot().find('form');\n const formData = form.serialize();\n\n // Now we can continue...\n Ajax.call([{\n methodname: 'core_form_dynamic_form',\n args: {\n formdata: formData,\n form: this.config.formClass\n }\n }])[0]\n .then((response) => {\n if (!response.submitted) {\n // Form was not submitted because validation failed.\n const promise = new Promise(\n resolve => resolve({html: response.html, js: Fragment.processCollectedJavascript(response.javascript)}));\n this.modal.setBodyContent(promise);\n this.enableButtons();\n this.trigger(this.events.SERVER_VALIDATION_ERROR);\n } else {\n // Form was submitted properly. Hide the modal and execute callback.\n const data = JSON.parse(response.data);\n FormChangeChecker.markFormSubmitted(form[0]);\n const event = this.trigger(this.events.FORM_SUBMITTED, data);\n if (!event.defaultPrevented) {\n this.modal.hide();\n }\n }\n return null;\n })\n .catch(exception => this.onSubmitError(exception));\n }\n\n /**\n * Set the classes for the 'save' button.\n *\n * @method setSaveButtonClasses\n * @param {(String)} value The 'save' button classes.\n */\n setSaveButtonClasses(value) {\n const button = this.modal.getFooter().find(\"[data-action='save']\");\n if (!button) {\n throw new Error(\"Unable to find the 'save' button\");\n }\n button.removeClass().addClass(value);\n }\n}\n"],"names":["constructor","config","FORM_SUBMITTED","FORM_CANCELLED","CLIENT_VALIDATION_ERROR","SERVER_VALIDATION_ERROR","ERROR","NOSUBMIT_BUTTON_PRESSED","SUBMIT_BUTTON_PRESSED","CANCEL_BUTTON_PRESSED","LOADED","modal","modalConfig","removeOnClose","type","ModalFactory","types","SAVE_CANCEL","large","this","args","futureListeners","show","pendingPromise","Pending","create","then","formParams","bodyContent","getBody","setBodyContent","catch","Notification","exception","getRoot","on","ModalEvents","hidden","notifyResetFormChanges","destroy","returnFocus","focus","getModal","addClass","e","preventDefault","trigger","events","target","defaultPrevented","processNoSubmitButton","submitFormAjax","saveButtonText","setSaveButtonText","saveButtonClasses","setSaveButtonClasses","save","find","submit","cancel","forEach","addEventListener","resolve","eventName","CustomEvent","detail","cancelable","dispatchEvent","push","formDataString","params","formdata","form","formClass","Ajax","call","methodname","response","html","js","Fragment","processCollectedJavascript","javascript","onSubmitError","getFormNode","FormEvents","notifyFormSubmittedByJavascript","FormChangeChecker","resetFormDirtyState","button","formData","serialize","encodeURIComponent","getAttribute","validateElements","invalid","length","first","disableButtons","getFooter","attr","enableButtons","removeAttr","submitted","data","JSON","parse","markFormSubmitted","hide","promise","Promise","value","Error","removeClass"],"mappings":"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"} \ No newline at end of file diff --git a/lib/form/amd/src/modalform.js b/lib/form/amd/src/modalform.js index 40a38b73a31..1d8b512ca60 100644 --- a/lib/form/amd/src/modalform.js +++ b/lib/form/amd/src/modalform.js @@ -269,13 +269,13 @@ export default class ModalForm { */ notifyResetFormChanges() { const form = this.getFormNode(); - FormEvents.notifyFormSubmittedByJavascript(this.getFormNode(), true); - if (!form) { return; } - FormChangeChecker.resetFormDirtyState(this.getFormNode()); + FormEvents.notifyFormSubmittedByJavascript(form, true); + + FormChangeChecker.resetFormDirtyState(form); } /** @@ -358,7 +358,8 @@ export default class ModalForm { this.disableButtons(); // Convert all the form elements values to a serialised string. - const formData = this.modal.getRoot().find('form').serialize(); + const form = this.modal.getRoot().find('form'); + const formData = form.serialize(); // Now we can continue... Ajax.call([{ @@ -379,7 +380,7 @@ export default class ModalForm { } else { // Form was submitted properly. Hide the modal and execute callback. const data = JSON.parse(response.data); - this.notifyResetFormChanges(); + FormChangeChecker.markFormSubmitted(form[0]); const event = this.trigger(this.events.FORM_SUBMITTED, data); if (!event.defaultPrevented) { this.modal.hide();