diff --git a/frontend/src/app/framework/angular/forms/editors/autocomplete.component.html b/frontend/src/app/framework/angular/forms/editors/autocomplete.component.html
index c514a90d5..6fdf61d71 100644
--- a/frontend/src/app/framework/angular/forms/editors/autocomplete.component.html
+++ b/frontend/src/app/framework/angular/forms/editors/autocomplete.component.html
@@ -22,7 +22,8 @@
[placeholder]="placeholder"
rows="2"
sqxAutosize
- [sqxFocusOnInit]="autoFocus">
+ [sqxFocusOnInit]="autoFocus"
+ sqxForwardFormClasses>
} @else {
>;
@@ -77,6 +78,7 @@ const RANGE_LIMIT = 60;
DropdownMenuComponent,
FocusOnInitDirective,
FormsModule,
+ ForwardFormClassesDirective,
LoaderComponent,
ModalDirective,
ModalPlacementDirective,
diff --git a/frontend/src/app/framework/angular/forms/editors/date-time-editor.component.html b/frontend/src/app/framework/angular/forms/editors/date-time-editor.component.html
index 5c2dac959..73db8c4df 100644
--- a/frontend/src/app/framework/angular/forms/editors/date-time-editor.component.html
+++ b/frontend/src/app/framework/angular/forms/editors/date-time-editor.component.html
@@ -28,6 +28,7 @@
(ngModelChange)="updateValue($event)"
[ngModelOptions]="{ standalone: true }"
placeholder="{{ 'common.date' | sqxTranslate }}"
+ sqxForwardFormClasses
[type]="isDateTimeMode ? 'datetime-local' : 'date'" />
diff --git a/frontend/src/app/framework/angular/forms/editors/date-time-editor.component.ts b/frontend/src/app/framework/angular/forms/editors/date-time-editor.component.ts
index 1dee0de9b..49f09a690 100644
--- a/frontend/src/app/framework/angular/forms/editors/date-time-editor.component.ts
+++ b/frontend/src/app/framework/angular/forms/editors/date-time-editor.component.ts
@@ -13,6 +13,7 @@ import { TooltipDirective } from '../../modals/tooltip.directive';
import { TranslatePipe } from '../../pipes/translate.pipe';
import { ResizedDirective } from '../../resized.directive';
import { FocusComponent } from '../forms-helper';
+import { ForwardFormClassesDirective } from '../forward-form-classes.directive';
export const SQX_DATE_TIME_EDITOR_CONTROL_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => DateTimeEditorComponent), multi: true,
@@ -39,6 +40,7 @@ interface State {
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
FormsModule,
+ ForwardFormClassesDirective,
ReactiveFormsModule,
ResizedDirective,
TooltipDirective,
diff --git a/frontend/src/app/framework/angular/forms/editors/dropdown.component.html b/frontend/src/app/framework/angular/forms/editors/dropdown.component.html
index 1f24539f9..126349711 100644
--- a/frontend/src/app/framework/angular/forms/editors/dropdown.component.html
+++ b/frontend/src/app/framework/angular/forms/editors/dropdown.component.html
@@ -10,7 +10,8 @@
[disabled]="snapshot.isDisabled"
(keydown)="onKeyDown($event)"
[name]="formName"
- readonly />
+ readonly
+ sqxForwardFormClasses />
@if (snapshot.selectedItem; as selectedItem) {
diff --git a/frontend/src/app/framework/angular/forms/editors/dropdown.component.ts b/frontend/src/app/framework/angular/forms/editors/dropdown.component.ts
index 3eadacfa3..7a67208a5 100644
--- a/frontend/src/app/framework/angular/forms/editors/dropdown.component.ts
+++ b/frontend/src/app/framework/angular/forms/editors/dropdown.component.ts
@@ -18,6 +18,7 @@ import { TranslatePipe } from '../../pipes/translate.pipe';
import { ScrollActiveDirective } from '../../scroll-active.directive';
import { TemplateWrapperDirective } from '../../template-wrapper.directive';
import { FocusOnInitDirective } from '../focus-on-init.directive';
+import { ForwardFormClassesDirective } from '../forward-form-classes.directive';
export const SQX_DROPDOWN_CONTROL_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => DropdownComponent), multi: true,
@@ -49,6 +50,7 @@ interface State {
DropdownMenuComponent,
FocusOnInitDirective,
FormsModule,
+ ForwardFormClassesDirective,
LoaderComponent,
ModalDirective,
ModalPlacementDirective,
diff --git a/frontend/src/app/framework/angular/forms/editors/tag-editor.component.html b/frontend/src/app/framework/angular/forms/editors/tag-editor.component.html
index 1513b38df..0690c4d62 100644
--- a/frontend/src/app/framework/angular/forms/editors/tag-editor.component.html
+++ b/frontend/src/app/framework/angular/forms/editors/tag-editor.component.html
@@ -12,6 +12,7 @@
[class.suggested]="itemsSorted.length > 0"
(focus)="focusInput($event)"
(mousedown)="focusInput($event)"
+ sqxForwardFormClasses
tabindex="0">
@for (tag of snapshot.tags; track tag; let i = $index) {
{{ tag }}
diff --git a/frontend/src/app/framework/angular/forms/editors/tag-editor.component.ts b/frontend/src/app/framework/angular/forms/editors/tag-editor.component.ts
index 96a3edd5b..9434d17de 100644
--- a/frontend/src/app/framework/angular/forms/editors/tag-editor.component.ts
+++ b/frontend/src/app/framework/angular/forms/editors/tag-editor.component.ts
@@ -18,6 +18,7 @@ import { TooltipDirective } from '../../modals/tooltip.directive';
import { TranslatePipe } from '../../pipes/translate.pipe';
import { ScrollActiveDirective } from '../../scroll-active.directive';
import { StopClickDirective } from '../../stop-click.directive';
+import { ForwardFormClassesDirective } from '../forward-form-classes.directive';
export const SQX_TAG_EDITOR_CONTROL_VALUE_ACCESSOR: any = {
provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => TagEditorComponent), multi: true,
@@ -49,6 +50,7 @@ interface State {
DropdownMenuComponent,
FormsModule,
LoaderComponent,
+ ForwardFormClassesDirective,
ModalDirective,
ModalPlacementDirective,
ReactiveFormsModule,
diff --git a/frontend/src/app/framework/angular/forms/form-row.component.ts b/frontend/src/app/framework/angular/forms/form-row.component.ts
index 8fa79e017..6f8929abf 100644
--- a/frontend/src/app/framework/angular/forms/form-row.component.ts
+++ b/frontend/src/app/framework/angular/forms/form-row.component.ts
@@ -6,8 +6,8 @@
*/
-import { booleanAttribute, ChangeDetectionStrategy, Component, HostBinding, Input } from '@angular/core';
-import { AbstractControl } from '@angular/forms';
+import { booleanAttribute, ChangeDetectionStrategy, Component, HostBinding, Input, Optional, SkipSelf } from '@angular/core';
+import { AbstractControl, FormGroupDirective } from '@angular/forms';
import { Types } from '@app/framework/internal';
import { MarkdownDirective } from '../markdown.directive';
import { TranslatePipe } from '../pipes/translate.pipe';
@@ -26,6 +26,13 @@ import { FormHintComponent } from './form-hint.component';
'[class.row]': '!vertical',
'[class.form-group-aligned]': 'centered',
},
+ viewProviders: [
+ {
+ provide: FormGroupDirective,
+ useFactory: (directive: FormGroupDirective) => directive,
+ deps: [[new Optional(), new SkipSelf(), FormGroupDirective]],
+ },
+ ],
imports: [
ControlErrorsComponent,
FormAlertComponent,
diff --git a/frontend/src/app/framework/angular/forms/forward-form-classes.directive.ts b/frontend/src/app/framework/angular/forms/forward-form-classes.directive.ts
new file mode 100644
index 000000000..06ae1f9bd
--- /dev/null
+++ b/frontend/src/app/framework/angular/forms/forward-form-classes.directive.ts
@@ -0,0 +1,37 @@
+/*
+ * Squidex Headless CMS
+ *
+ * @license
+ * Copyright (c) Squidex UG (haftungsbeschränkt). All rights reserved.
+ */
+
+import { Directive, DoCheck, ElementRef, Injector, OnInit } from "@angular/core";
+import { NgControl } from "@angular/forms";
+
+@Directive({
+ selector: '[sqxForwardFormClasses]',
+})
+export class ForwardFormClassesDirective implements OnInit, DoCheck {
+ private ngControl: NgControl | null = null;
+
+ constructor(
+ private readonly injector: Injector,
+ private readonly host: ElementRef,
+ ) {
+ }
+
+ public ngOnInit() {
+ this.ngControl = this.injector.get(NgControl, null);
+ }
+
+ public ngDoCheck() {
+ const classes = this.host.nativeElement.classList;
+ classes.toggle('ng-touched', !!this.ngControl?.touched);
+ classes.toggle('ng-untouched', !!this.ngControl?.untouched);
+ classes.toggle('ng-dirty', !!this.ngControl?.dirty);
+ classes.toggle('ng-pristine', !!this.ngControl?.pristine);
+ classes.toggle('ng-valid', !!this.ngControl?.valid);
+ classes.toggle('ng-invalid', !!this.ngControl?.invalid);
+ classes.toggle('ng-pending', !!this.ngControl?.pending);
+ }
+}
\ No newline at end of file
diff --git a/frontend/src/app/shared/state/schemas.forms.ts b/frontend/src/app/shared/state/schemas.forms.ts
index 7bf97af26..19feffae7 100644
--- a/frontend/src/app/shared/state/schemas.forms.ts
+++ b/frontend/src/app/shared/state/schemas.forms.ts
@@ -422,7 +422,7 @@ export class EditSchemaForm extends Form