diff --git a/alfa-client/libs/admin/keycloak-shared/src/lib/keycloak-formservice.ts b/alfa-client/libs/admin/keycloak-shared/src/lib/keycloak-formservice.ts index e1856df17c3952c3fde88e15d205b6838b9083ff..2e628b17e00b17153893c8c9d909c99bf90c95e2 100644 --- a/alfa-client/libs/admin/keycloak-shared/src/lib/keycloak-formservice.ts +++ b/alfa-client/libs/admin/keycloak-shared/src/lib/keycloak-formservice.ts @@ -21,7 +21,13 @@ * Die sprachspezifischen Genehmigungen und Beschränkungen * unter der Lizenz sind dem Lizenztext zu entnehmen. */ -import { createEmptyStateResource, InvalidParam, isLoaded, setInvalidParamValidationError, StateResource, } from '@alfa-client/tech-shared'; +import { + createEmptyStateResource, + InvalidParam, + isLoaded, + setInvalidParamValidationError, + StateResource, +} from '@alfa-client/tech-shared'; import { inject, Injectable } from '@angular/core'; import { AbstractControl, FormArray, FormBuilder, FormGroup } from '@angular/forms'; import { ActivatedRoute, UrlSegment } from '@angular/router'; @@ -72,16 +78,24 @@ export abstract class KeycloakFormService<T> { } public submit(): Observable<StateResource<T>> { + console.info('value before update and validity: %o', this.form.value); + this._showValidationErrorForAllInvalidControls(this.form); + console.info('value after update and validity: %o', this.form.value); + setTimeout(() => { + console.info('form after timeout valid = %s, value = %o', this.form.valid, this.form.value); + }, 0); if (this.form.invalid) { + console.info('form INVALID'); return this._processInvalidForm(); } + console.info('form VALID'); return this._doSubmit().pipe( catchError((keycloakError: KeycloakHttpErrorResponse) => this._processResponseValidationErrors(keycloakError)), ); } _processInvalidForm(): Observable<StateResource<T>> { - this._showValidationErrorForAllInvalidControls(this.form); + // this._showValidationErrorForAllInvalidControls(this.form); return of(createEmptyStateResource<T>()); } @@ -98,7 +112,8 @@ export abstract class KeycloakFormService<T> { } _showValidationErrorForAllInvalidControls(control: AbstractControl): void { - if (control.invalid) control.updateValueAndValidity(); + // if (control.invalid) + control.updateValueAndValidity(); if (control instanceof FormGroup || control instanceof FormArray) { Object.values(control.controls).forEach((control) => this._showValidationErrorForAllInvalidControls(control)); } diff --git a/alfa-client/libs/admin/user/src/lib/user-form/user-form.component.html b/alfa-client/libs/admin/user/src/lib/user-form/user-form.component.html index 132dc6ba2c8eb40cfc12c0f163ba94092afca6a2..6b840fbfea6231c914316368d43a70c433484d83 100644 --- a/alfa-client/libs/admin/user/src/lib/user-form/user-form.component.html +++ b/alfa-client/libs/admin/user/src/lib/user-form/user-form.component.html @@ -23,20 +23,22 @@ unter der Lizenz sind dem Lizenztext zu entnehmen. --> -<ods-spinner [stateResource]="userStateResource$ | async"> - <div class="max-w-[960px]" data-test-id="user-content"> - <admin-user-form-headline [isPatch]="isPatch" /> - <admin-user-form-data [formGroupParent]="formService.form" [isPatch]="isPatch" [userName]="userName" /> - <admin-user-form-roles [formGroupParent]="formService.form" /> - <admin-user-form-organisations-einheit-list - [formGroupParent]="formService.form" - [formGroupOrganisationsEinheiten]="formService.getOrganisationsEinheitenGroup()" - /> - <div class="mb-6 flex justify-between"> - <admin-user-form-save-button /> - @if (isPatch) { - <admin-delete-open-dialog-button data-test-id="delete-button-container" /> - } +<form [formGroup]="formService.form"> + <ods-spinner [stateResource]="userStateResource$ | async"> + <div class="max-w-[960px]" data-test-id="user-content"> + <admin-user-form-headline [isPatch]="isPatch" /> + <admin-user-form-data [formGroupParent]="formService.form" [isPatch]="isPatch" [userName]="userName" /> + <admin-user-form-roles [formGroupParent]="formService.form" /> + <admin-user-form-organisations-einheit-list + [formGroupParent]="formService.form" + [formGroupOrganisationsEinheiten]="formService.getOrganisationsEinheitenGroup()" + /> + <div class="mb-6 flex justify-between"> + <admin-user-form-save-button /> + @if (isPatch) { + <admin-delete-open-dialog-button data-test-id="delete-button-container" /> + } + </div> </div> - </div> -</ods-spinner> + </ods-spinner> +</form> diff --git a/alfa-client/libs/admin/user/src/lib/user-form/user.formservice.ts b/alfa-client/libs/admin/user/src/lib/user-form/user.formservice.ts index b0cb7867c9cd8330c274fd9ee944d16f9a5507bb..9b7cea47cd6d0c753c02546a83f9969d6e543a08 100644 --- a/alfa-client/libs/admin/user/src/lib/user-form/user.formservice.ts +++ b/alfa-client/libs/admin/user/src/lib/user-form/user.formservice.ts @@ -32,19 +32,10 @@ import { PatchConfig, } from '@admin/keycloak-shared'; import { NavigationService } from '@alfa-client/navigation-shared'; -import { - checkBoxGroupsEmptyValidator, - EMPTY_STRING, - fieldEmptyValidator, - fieldInvalidValidator, - fieldLengthValidator, - isLoaded, - mapToResource, - StateResource, -} from '@alfa-client/tech-shared'; +import { EMPTY_STRING, isLoaded, mapToResource, StateResource } from '@alfa-client/tech-shared'; import { SnackBarService } from '@alfa-client/ui'; import { Injectable, OnDestroy } from '@angular/core'; -import { AbstractControl, FormControl, UntypedFormBuilder, UntypedFormGroup } from '@angular/forms'; +import { AbstractControl, FormControl, UntypedFormBuilder, UntypedFormGroup, Validators } from '@angular/forms'; import { UrlSegment } from '@angular/router'; import { filter, Observable, Subscription, tap } from 'rxjs'; @@ -100,15 +91,13 @@ export class UserFormService extends KeycloakFormService<User> implements OnDest } _initForm(): UntypedFormGroup { - return this.formBuilder.group({ - [UserFormService.FIRST_NAME]: new FormControl(EMPTY_STRING, fieldEmptyValidator(UserFormService.FIRST_NAME)), - [UserFormService.LAST_NAME]: new FormControl(EMPTY_STRING, fieldEmptyValidator(UserFormService.LAST_NAME)), - [UserFormService.USERNAME]: new FormControl(EMPTY_STRING, [fieldLengthValidator(UserFormService.USERNAME, 3, 255)]), - [UserFormService.EMAIL]: new FormControl(EMPTY_STRING, [ - fieldInvalidValidator(UserFormService.EMAIL, /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/), - ]), - [UserFormService.CLIENT_ROLES]: this.formBuilder.group( - { + return this.formBuilder.group( + { + [UserFormService.FIRST_NAME]: new FormControl(EMPTY_STRING, Validators.required), + [UserFormService.LAST_NAME]: new FormControl(EMPTY_STRING), + [UserFormService.USERNAME]: new FormControl(EMPTY_STRING), + [UserFormService.EMAIL]: new FormControl(EMPTY_STRING), + [UserFormService.CLIENT_ROLES]: this.formBuilder.group({ [UserFormService.ADMINISTRATION_GROUP]: this.formBuilder.group({ [UserFormService.ADMIN]: new FormControl(false), [UserFormService.DATENBEAUFTRAGUNG]: new FormControl(false), @@ -118,16 +107,11 @@ export class UserFormService extends KeycloakFormService<User> implements OnDest [UserFormService.USER]: new FormControl(false), [UserFormService.POSTSTELLE]: new FormControl(false), }), - }, - { - validators: checkBoxGroupsEmptyValidator(UserFormService.CLIENT_ROLES, [ - UserFormService.ADMINISTRATION_GROUP, - UserFormService.ALFA_GROUP, - ]), - }, - ), - [UserFormService.GROUPS]: this.formBuilder.group({}), - }); + }), + [UserFormService.GROUPS]: this.formBuilder.group({}), + }, + { updateOn: 'submit' }, + ); } _initOrganisationsEinheiten(): Observable<AdminOrganisationsEinheit[]> { diff --git a/alfa-client/libs/design-component/src/lib/form/formcontrol-editor.abstract.component.ts b/alfa-client/libs/design-component/src/lib/form/formcontrol-editor.abstract.component.ts index 89c0cd5fb117bc5c11c2aa254dfd810ec7616b32..775483fcac0b16da717e41dfd9632970bfdde5a6 100644 --- a/alfa-client/libs/design-component/src/lib/form/formcontrol-editor.abstract.component.ts +++ b/alfa-client/libs/design-component/src/lib/form/formcontrol-editor.abstract.component.ts @@ -45,13 +45,20 @@ export abstract class FormControlEditorAbstractComponent implements ControlValue this.changesSubscr = this.fieldControl.valueChanges.subscribe((val) => { this.onChange(val); - this.setErrors(); + // if (this.control?.control && this.control.control.getRawValue() !== this.fieldControl.value) { + // this.control.control.setValue(this.fieldControl.value, { emitEvent: false }); + // } + this.fieldControl.setErrors(null); + this.invalidParams = []; }); } ngOnInit(): void { if (!this.statusSubscr && this.control) this.statusSubscr = this.control.statusChanges.subscribe(() => { + this.onChange(this.fieldControl.value); + this.fieldControl.updateValueAndValidity({ emitEvent: false }); + this.control.control.updateValueAndValidity({ emitEvent: false }); this.setErrors(); }); } @@ -62,7 +69,6 @@ export abstract class FormControlEditorAbstractComponent implements ControlValue writeValue(text: string): void { this.fieldControl.setValue(text); - this.setErrors(); } registerOnChange(fn: (text: string | Date) => {}): void { diff --git a/alfa-client/libs/design-system/src/lib/button/button.component.ts b/alfa-client/libs/design-system/src/lib/button/button.component.ts index 3f88a32abc1bdb41fc4c12cabc4543f7c2def3c1..78bc43acf3056cc5d13782d7d413008764dfb176 100644 --- a/alfa-client/libs/design-system/src/lib/button/button.component.ts +++ b/alfa-client/libs/design-system/src/lib/button/button.component.ts @@ -104,8 +104,8 @@ export type ButtonVariants = VariantProps<typeof buttonVariants>; selector: 'ods-button', standalone: true, imports: [CommonModule, SpinnerIconComponent], - template: `<button - type="button" + template: ` <button + type="submit" [ngClass]="buttonVariants({ size, variant, disabled: isDisabled, destructive })" [attr.aria-disabled]="isDisabled" [attr.aria-label]="text"