diff --git a/src/ValidationsFactory.js b/src/ValidationsFactory.js index 23e0210e..5c67eacd 100644 --- a/src/ValidationsFactory.js +++ b/src/ValidationsFactory.js @@ -235,6 +235,15 @@ class PageNavigateValidations extends Validations { } } +/** + * No-op validations (e.g. FormRecordList — modal fields validate separately) + */ +class NoOpValidations extends Validations { + async addValidations() { + // intentionally empty + } +} + /** * Add validations for a form element */ @@ -400,8 +409,9 @@ function ValidationsFactory(element, options) { return new FormLoopValidations(element, options); } if (element.component === 'FormRecordList') { - //not required - //return new FormRecordListValidations(element, screen); + // Record list modal fields are validated only when submitting the modal, + // not as part of the parent screen's validation rules. + return new NoOpValidations(element, options); } if (element.component === 'FormButton' && element.config.event === 'pageNavigate') { return new PageNavigateValidations(element, options); diff --git a/src/components/renderer/form-record-list.vue b/src/components/renderer/form-record-list.vue index e76faaa3..b6de26ec 100644 --- a/src/components/renderer/form-record-list.vue +++ b/src/components/renderer/form-record-list.vue @@ -182,6 +182,7 @@ :current-page="form" :computed="formComputed" :watchers="formWatchers" + :isolated="true" debug-context="Record List Add" :_parent="validationData" @update="updateRowDataNamePrefix" @@ -198,7 +199,7 @@ header-close-content="×" data-cy="modal-edit" @ok="edit" - @hidden="$refs.addRenderer.hasSubmitted(false)" + @hidden="handleHideEditModal" @shown="emitShownEvent" > import _ from "lodash"; +import { mapActions } from "vuex"; import { dateUtils } from "@processmaker/vue-form-elements"; import VueFormRenderer from "@/components/vue-form-renderer.vue"; import mustacheEvaluation from "../../mixins/mustacheEvaluation"; import MustacheHelper from "../inspector/mustache-helper.vue"; import Mustache from "mustache"; +import { findRootScreen } from "@/mixins/DataReference"; const jsonOptionsActionsColumn = { key: "__actions", @@ -507,6 +511,7 @@ export default { this.$root.$emit("record-list-option", this.source?.sourceOptions); }, methods: { + ...mapActions("globalErrorsModule", ["validateNow", "close"]), togglePopover(index, event, rowId) { this.deleteIndex = _.find(this.tableData.data, { row_id: rowId }); this.isPopoverVisible = this.isPopoverVisible === index ? null : index; @@ -991,7 +996,7 @@ export default { }); }, edit(event) { - this.$refs.addRenderer.hasSubmitted(true); + this.$refs.editRenderer.hasSubmitted(true); if ( this.$refs.editRenderer.$refs.renderer.$refs.component.$v.vdata.$invalid ) { @@ -1030,7 +1035,25 @@ export default { }, handleHideAddModal() { this.addItem = this.initFormValues; - this.$refs.addRenderer.hasSubmitted(false); + if (this.$refs.addRenderer) { + this.$refs.addRenderer.hasSubmitted(false); + } + this.restoreParentValidationState(); + }, + handleHideEditModal() { + if (this.$refs.editRenderer) { + this.$refs.editRenderer.hasSubmitted(false); + } + this.restoreParentValidationState(); + }, + restoreParentValidationState() { + // Clear modal-driven global submit flags and refresh parent validity + // so required modal fields (e.g. FormCheckbox) never block parent submit. + this.close(); + const rootScreen = findRootScreen(this); + if (rootScreen && typeof rootScreen.loadValidationRules === "function") { + this.validateNow(rootScreen); + } }, async handleOk(bvModalEvt) { this.$refs.addRenderer.hasSubmitted(true); diff --git a/src/components/vue-form-renderer.vue b/src/components/vue-form-renderer.vue index 26fc57cc..35957a35 100644 --- a/src/components/vue-form-renderer.vue +++ b/src/components/vue-form-renderer.vue @@ -64,7 +64,10 @@ export default { "showErrors", "testScreenDefinition", "deviceScreen", - "taskdraft" + "taskdraft", + // When true, local $v is still used (e.g. record list modals) but data + // changes do not update the parent form's global valid/submit state. + "isolated" ], data() { return { @@ -152,6 +155,9 @@ export default { deep: true, handler() { this.$emit("update", this.data); + if (this.isolated) { + return; + } const mainScreen = this.getMainScreen(); if (mainScreen) { this.validate(mainScreen);