diff --git a/client/src/app/shared/validators/one-of-validator.ts b/client/src/app/shared/validators/one-of-validator.ts new file mode 100644 index 000000000..1c11cceff --- /dev/null +++ b/client/src/app/shared/validators/one-of-validator.ts @@ -0,0 +1,27 @@ +import { ValidationErrors, AbstractControl } from '@angular/forms'; + +/** + * Custom validator class + * Several strings can be passed as `keys` of the form group. + * This validator ensures that at least one of the fields behind the given keys has a value. + */ +export class OneOfValidator { + /** + * Function to confirm that at least one of the given form controls has a valid value. + * + * @param keys Undefined amount of form control names. + * + * @returns An error if all of these form controls have no valid values or null if at least one is filled. + */ + public static validation(...keys: string[]): (control: AbstractControl) => ValidationErrors | null { + return (control: AbstractControl): ValidationErrors | null => { + const formControls = keys.map(key => control.get(key)); + + const noOneSet = formControls.every( + formControl => (formControl && formControl.value === '') || !formControl + ); + + return noOneSet ? { noOneSet: true } : null; + }; + } +} diff --git a/client/src/app/site/users/components/user-detail/user-detail.component.ts b/client/src/app/site/users/components/user-detail/user-detail.component.ts index 75be902ad..54fd763cc 100644 --- a/client/src/app/site/users/components/user-detail/user-detail.component.ts +++ b/client/src/app/site/users/components/user-detail/user-detail.component.ts @@ -15,6 +15,7 @@ import { UserRepositoryService } from 'app/core/repositories/users/user-reposito import { ViewUser } from '../../models/view-user'; import { ViewGroup } from '../../models/view-group'; import { GroupRepositoryService } from 'app/core/repositories/users/group-repository.service'; +import { OneOfValidator } from 'app/shared/validators/one-of-validator'; /** * Users detail component for both new and existing users @@ -226,24 +227,29 @@ export class UserDetailComponent extends BaseViewComponent implements OnInit { * initialize the form with default values */ public createForm(): void { - this.personalInfoForm = this.formBuilder.group({ - username: [''], - title: [''], - first_name: [''], - last_name: [''], - gender: [''], - structure_level: [''], - number: [''], - about_me: [''], - groups_id: [''], - is_present: [true], - is_committee: [false], - email: ['', Validators.email], - last_email_send: [''], - comment: [''], - is_active: [true], - default_password: [''] - }); + this.personalInfoForm = this.formBuilder.group( + { + username: [''], + title: [''], + first_name: [''], + last_name: [''], + gender: [''], + structure_level: [''], + number: [''], + about_me: [''], + groups_id: [''], + is_present: [true], + is_committee: [false], + email: ['', Validators.email], + last_email_send: [''], + comment: [''], + is_active: [true], + default_password: [''] + }, + { + validators: OneOfValidator.validation('username', 'first_name', 'last_name') + } + ); // patch the form only for existing users if (!this.newUser) { @@ -342,11 +348,26 @@ export class UserDetailComponent extends BaseViewComponent implements OnInit { * Save / Submit a user */ public async saveUser(): Promise { + if (this.personalInfoForm.invalid) { + let hint = ''; + if (this.personalInfoForm.errors) { + hint = 'At least one of username, first name or last name has to be set.'; + } else { + for (const formControl in this.personalInfoForm.controls) { + if (this.personalInfoForm.get(formControl).errors) { + hint = formControl + ' is incorrect.'; + } + } + } + this.raiseError(this.translate.instant(hint)); + return; + } try { if (this.newUser) { - this.newUser = false; - await this.repo.create(this.personalInfoForm.value); - this.router.navigate([`./users/`]); + await this.repo.create(this.personalInfoForm.value).then(() => { + this.newUser = false; + this.router.navigate([`./users/`]); + }, this.raiseError); } else { // TODO (Issue #3962): We need a waiting-State, so if autoupdates come before the response, // the user is also updated.