From 596af309a87137fdd219cccc232033174f3a1bc0 Mon Sep 17 00:00:00 2001 From: Rob Luton Date: Thu, 19 Feb 2026 15:51:05 -0600 Subject: [PATCH 1/2] inject inline-repeater errors into main form validation errors --- .../inline-repeater-interface/package.json | 2 +- .../inline-repeater-interface/src/list.vue | 53 +++++++++++++++++-- 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/packages/inline-repeater-interface/package.json b/packages/inline-repeater-interface/package.json index 62a469fb..4289477b 100644 --- a/packages/inline-repeater-interface/package.json +++ b/packages/inline-repeater-interface/package.json @@ -1,7 +1,7 @@ { "name": "@directus-labs/inline-repeater-interface", "type": "module", - "version": "1.0.1", + "version": "1.0.2", "description": "A powerful interface for managing repeatable form fields within Directus that allows inline editing and reordering of items.", "license": "MIT", "repository": { diff --git a/packages/inline-repeater-interface/src/list.vue b/packages/inline-repeater-interface/src/list.vue index 84373c37..c0665e12 100644 --- a/packages/inline-repeater-interface/src/list.vue +++ b/packages/inline-repeater-interface/src/list.vue @@ -8,13 +8,14 @@ import { AccordionRoot, AccordionTrigger, } from 'reka-ui'; -import { computed, nextTick, ref, toRefs } from 'vue'; +import { computed, inject, nextTick, ref, toRefs, watch } from 'vue'; import { useI18n } from 'vue-i18n'; import Draggable from 'vuedraggable'; const props = withDefaults( defineProps<{ value: Record[] | null; + field?: string; fields?: DeepPartial[]; template?: string; addLabel?: string; @@ -161,8 +162,54 @@ function onDragEnd(evt: any) { const itemToRemove = ref(null); -// eslint-disable-next-line unused-imports/no-unused-vars -const validationErrors = ref([]); +const { updateNestedValidationErrors } = inject<{ + updateNestedValidationErrors: (field: string, errors: any[]) => void; +}>('nestedValidation', { updateNestedValidationErrors: () => {} }); + +const itemValidationErrors = computed>(() => { + const errorsMap: Record = {}; + + internalValue.value?.forEach((item, index) => { + const errors: any[] = []; + + for (const field of props.fields ?? []) { + if (!field.field || !field.meta?.required) continue; + + const val = item[field.field]; + const isEmpty = val === null || val === undefined || val === '' || (Array.isArray(val) && val.length === 0); + + if (isEmpty) { + errors.push({ field: field.field, type: 'nnull' }); + } + } + + if (errors.length > 0) errorsMap[index] = errors; + }); + + return errorsMap; +}); + +watch(itemValidationErrors, (errorsMap) => { + if (!props.field) return; + + const allErrors = Object.entries(errorsMap).flatMap(([indexStr, errors]) => { + const index = Number(indexStr); + return errors.map((error) => { + const fieldDef = props.fields?.find((f) => f.field === error.field); + return { + ...error, + // Renders as: "Repeater Field Name → [index] → Sub-field Name" + field: `${props.field}.${index}.${error.field}`, + nestedNames: { + [String(index)]: `[${index + 1}]`, + [error.field]: fieldDef?.name ?? fieldDef?.field ?? error.field, + }, + }; + }); + }); + + updateNestedValidationErrors(props.field, allErrors); +}, { immediate: true }); const confirmDiscard = ref(false); From 52587fdc4761eb9956d9805a7c75a04d0de520c2 Mon Sep 17 00:00:00 2001 From: Rob Luton Date: Fri, 20 Feb 2026 11:28:49 -0600 Subject: [PATCH 2/2] use zero indexing in validation message --- packages/inline-repeater-interface/src/list.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/inline-repeater-interface/src/list.vue b/packages/inline-repeater-interface/src/list.vue index c0665e12..0c9dc1c5 100644 --- a/packages/inline-repeater-interface/src/list.vue +++ b/packages/inline-repeater-interface/src/list.vue @@ -201,7 +201,7 @@ watch(itemValidationErrors, (errorsMap) => { // Renders as: "Repeater Field Name → [index] → Sub-field Name" field: `${props.field}.${index}.${error.field}`, nestedNames: { - [String(index)]: `[${index + 1}]`, + [String(index)]: `[${index}]`, [error.field]: fieldDef?.name ?? fieldDef?.field ?? error.field, }, };