You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Note: This template is intended for Engineering team use.
Description
Custom fields (VTL-based code injected into the DOM) that are marked as required do not display any validation alert or error indicator when the form is submitted or the field is touched. Unlike Angular-native fields — which use BaseWrapperField.$hasError and render a "This field is mandatory" message via p-field-error — custom fields have no mechanism to query or react to the host form's validation state.
Root cause
The FormFieldAPI interface (exposed to custom field code as DotCustomFieldApi.getField(fieldId)) currently only provides:
getValue() / setValue() — read/write field values
onChange() — subscribe to value changes
enable() / disable() — toggle field enabled state
show() / hide() — toggle field visibility
No validation-related methods exist — there is no way for custom field code to know whether a field is valid, required, touched, or dirty, nor to subscribe to validation state changes.
Sets iframeWindow.DotCustomFieldApi for iframe strategy
VTL custom fields
e.g. dotCMS/src/main/webapp/WEB-INF/velocity/static/htmlpage_assets/url-title_new.vtl
Consumer example
Expected API additions
The following methods should be added to FormFieldAPI:
interfaceFormFieldAPI{// ... existing methods .../** Returns true if the field's FormControl is valid */isValid(): boolean;/** Returns true if the field is required (via field metadata or Validators.required) */isRequired(): boolean;/** Returns true if the field's FormControl has been touched */isTouched(): boolean;/** Returns true if the field's FormControl is dirty */isDirty(): boolean;/** Returns the current validation errors object, or null if valid */getErrors(): Record<string,unknown>|null;/** * Subscribes to validation status changes (valid ↔ invalid transitions). * Callback receives the current validation state. * Returns an unsubscribe function (consistent with onChange() pattern). */onStatusChange(callback: (state: {valid: boolean;errors: Record<string,unknown>|null})=>void): ()=>void;}
Implementation approach
In AngularFormBridge.getField(), each new method should delegate to the underlying Angular AbstractControl:
isValid() → control.valid
isRequired() → field.required or control.hasValidator(Validators.required)
isTouched() → control.touched
isDirty() → control.dirty
getErrors() → control.errors
onStatusChange() → subscribe to control.statusChanges observable, following the same unsubscribe pattern as onChange()
Usage example (VTL custom field)
DotCustomFieldApi.ready(()=>{constmyField=DotCustomFieldApi.getField('urlTitle');// Check initial stateif(myField.isRequired()&&!myField.isValid()){showError('This field is required');}// React to validation changesmyField.onStatusChange(({ valid, errors })=>{if(!valid){showError('Validation failed');}else{hideError();}});});
Acceptance Criteria
FormFieldAPI interface includes new methods: isValid(), isRequired(), isTouched(), isDirty(), getErrors(), and onStatusChange(callback)
AngularFormBridge.getField() implementation returns a FormFieldAPI where each new method delegates to the underlying Angular AbstractControl properties (valid, touched, dirty, errors, statusChanges)
onStatusChange(callback) subscribes to the Angular AbstractControl.statusChanges observable and executes the callback with the current validation state ({ valid, errors }) whenever the status changes
onStatusChange() returns an unsubscribe function, consistent with the existing onChange() pattern
Custom field VTL code can call DotCustomFieldApi.getField('fieldVar').isValid() and receive a boolean indicating the current validation state
Custom field VTL code can call DotCustomFieldApi.getField('fieldVar').onStatusChange(cb) and the callback fires when the field transitions between valid and invalid states
Validation state is exposed in both native-field and iframe-field strategies (both set DotCustomFieldApi on the respective window)
Existing FormFieldAPI consumers (getValue, setValue, onChange, enable, disable, show, hide) are not affected by the additions — full backward compatibility
Unit tests cover all new FormFieldAPI methods in AngularFormBridge (valid, invalid, touched, dirty, errors, statusChange subscription and unsubscription)
Additional Context
Currently, AngularFormBridge.set() already calls markAsTouched(), markAsDirty(), and updateValueAndValidity() on the control — so programmatic updates from VTL code do affect Angular form state. The gap is that there is no way for the VTL code to read that state back or subscribe to changes.
Note: This template is intended for Engineering team use.
Description
Custom fields (VTL-based code injected into the DOM) that are marked as required do not display any validation alert or error indicator when the form is submitted or the field is touched. Unlike Angular-native fields — which use
BaseWrapperField.$hasErrorand render a "This field is mandatory" message viap-field-error— custom fields have no mechanism to query or react to the host form's validation state.Root cause
The
FormFieldAPIinterface (exposed to custom field code asDotCustomFieldApi.getField(fieldId)) currently only provides:getValue()/setValue()— read/write field valuesonChange()— subscribe to value changesenable()/disable()— toggle field enabled stateshow()/hide()— toggle field visibilityNo validation-related methods exist — there is no way for custom field code to know whether a field is valid, required, touched, or dirty, nor to subscribe to validation state changes.
Affected components
FormFieldAPIinterfacecore-web/libs/edit-content-bridge/src/lib/interfaces/form-bridge.interface.tsAngularFormBridgecore-web/libs/edit-content-bridge/src/lib/bridges/angular-form-bridge.tsFormGroupcontrols)NativeFieldComponentcore-web/libs/edit-content/src/lib/fields/dot-edit-content-custom-field/components/native-field/window.DotCustomFieldApifor native strategyIframeFieldComponentcore-web/libs/edit-content/src/lib/fields/dot-edit-content-custom-field/components/iframe-field/iframeWindow.DotCustomFieldApifor iframe strategydotCMS/src/main/webapp/WEB-INF/velocity/static/htmlpage_assets/url-title_new.vtlExpected API additions
The following methods should be added to
FormFieldAPI:Implementation approach
In
AngularFormBridge.getField(), each new method should delegate to the underlying AngularAbstractControl:isValid()→control.validisRequired()→field.requiredorcontrol.hasValidator(Validators.required)isTouched()→control.touchedisDirty()→control.dirtygetErrors()→control.errorsonStatusChange()→ subscribe tocontrol.statusChangesobservable, following the same unsubscribe pattern asonChange()Usage example (VTL custom field)
Acceptance Criteria
FormFieldAPIinterface includes new methods:isValid(),isRequired(),isTouched(),isDirty(),getErrors(), andonStatusChange(callback)AngularFormBridge.getField()implementation returns aFormFieldAPIwhere each new method delegates to the underlying AngularAbstractControlproperties (valid,touched,dirty,errors,statusChanges)onStatusChange(callback)subscribes to the AngularAbstractControl.statusChangesobservable and executes the callback with the current validation state ({ valid, errors }) whenever the status changesonStatusChange()returns an unsubscribe function, consistent with the existingonChange()patternDotCustomFieldApi.getField('fieldVar').isValid()and receive a boolean indicating the current validation stateDotCustomFieldApi.getField('fieldVar').onStatusChange(cb)and the callback fires when the field transitions between valid and invalid statesDotCustomFieldApion the respectivewindow)FormFieldAPIconsumers (getValue,setValue,onChange,enable,disable,show,hide) are not affected by the additions — full backward compatibilityFormFieldAPImethods inAngularFormBridge(valid, invalid, touched, dirty, errors, statusChange subscription and unsubscription)Additional Context
Currently,
AngularFormBridge.set()already callsmarkAsTouched(),markAsDirty(), andupdateValueAndValidity()on the control — so programmatic updates from VTL code do affect Angular form state. The gap is that there is no way for the VTL code to read that state back or subscribe to changes.