In my earlier posts, we discussed how to enable Composite MultiField in Content Fragments and how to enable dynamic data fields in the new Content Fragment editor. In this post, we will explore how to enable custom validations for Content Fragment fields. Most of the steps are similar to those outlined in the previous posts. You will create a field in the Content Fragment model, and using the field name, you will register the extension. Please refer to one of the earlier posts for a step-by-step guide to enabling the extension.
While creating a Content Fragment Model, you can set up various out-of-the-box (OOTB) validations for the CF fields, such as MaxLength and Required. These validations should be applied to the overridden fields by fetching the configurations from the model. Additionally, other validations like Email, URL, and Regex can also be applied to the fields from the model.
Note: Please be aware that the content of this blog does not reflect the views of Adobe or my current organization. Before applying this approach, make sure to validate it thoroughly and ensure that it aligns with Adobe's recommendations.
Now, additional validations can be applied through the extension. The out-of-the-box (OOTB) validations, such as Email, URL, and custom regex validations, are applied first, followed by custom validations. For example, if I enable Email validation, the field will only accept valid email addresses. Then, I can add another custom validation rule to reject certain predefined emails, such as
[email protected]
. This can be achieved through custom regex, but I’m just using this as an example for the demo.
Extension Component to enable additional custom validation Rules:
CustomFieldValidation.js
import React, { useEffect, useState } from "react";
import { attach } from "@adobe/uix-guest";
import { extensionId } from "./Constants";
import { TextField, Provider, View, defaultTheme } from "@adobe/react-spectrum";
const CustomFieldValidation = () => {
const [connection, setConnection] = useState(null);
const [model, setModel] = useState(null);
const [value, setValue] = useState("");
const [customError, setCustomError] = useState(null);
const [isInvalid, setIsInvalid] = useState(false);
const [validationInProgress, setValidationInProgress] = useState(false);
const validate = (val) => {
if (!connection?.host?.field) return;
let error = null;
// Custom validation rule
}
setCustomError(error);
setIsInvalid(!!error);
if (!error || validationInProgress) return;
setValidationInProgress(true);
// Delay call to allow host readiness
setTimeout(() => {
try {
connection.host.field
.setValidationState({ state: "invalid", message: error })
.catch((err) => {
console.warn(
"setValidationState failed:",
err?.message || JSON.stringify(err)
);
})
.finally(() => setValidationInProgress(false));
} catch (err) {
console.warn("setValidationState threw:", err?.message || JSON.stringify(err));
setValidationInProgress(false);
}
}, 1000); // 1s delay for stability
};
const handleChange = (val) => {
setValue(val);
try {
connection?.host?.field?.onChange(val).catch((err) =>
console.warn("onChange failed:", err?.message || JSON.stringify(err))
);
} catch (err) {
console.warn("onChange threw:", err?.message || JSON.stringify(err));
}
validate(val);
};
useEffect(() => {
const init = async () => {
try {
if (!extensionId) {
throw new Error("Missing extensionId. Check Constants file.");
}
const conn = await attach({ id: extensionId });
if (!conn?.host?.field) {
throw new Error("Host field API is unavailable.");
}
setConnection(conn);
const modelData = await conn.host.field.getModel();
setModel(modelData);
const defaultValue = (await conn.host.field.getDefaultValue()) || "";
setValue(defaultValue);
} catch (err) {
console.error("Extension init failed:", err?.message || JSON.stringify(err));
}
};
init();
}, []);
if (!connection || !model) {
return (
<Provider theme={defaultTheme}>
<View padding="size-200">Loading custom field…</View>
</Provider>
);
}
return (
<Provider theme={defaultTheme}>
<View padding="size-200" width="100%">
<TextField
label={model?.fieldLabel || "Custom Field"}
value={value}
onChange={handleChange}
isRequired={model?.required || false}
placeholder={model?.emptyText || "Enter a value"}
validationState={isInvalid ? "invalid" : undefined}
errorMessage={model?.customErrorMsg || customError}
maxLength={model?.maxLength || undefined}
width="100%"
/>
</View>
</Provider>
);
};
export default CustomFieldValidation;
Now the custom validation Rules are executed