mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-08 10:34:15 +00:00
[CST-4634] Change email validator in order to show error also on focus
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { Component, EventEmitter, OnDestroy, OnInit, Output } from '@angular/core';
|
import { ChangeDetectorRef, Component, EventEmitter, OnDestroy, OnInit, Output } from '@angular/core';
|
||||||
import { FormGroup, Validators } from '@angular/forms';
|
import { FormGroup } from '@angular/forms';
|
||||||
import {
|
import {
|
||||||
DynamicCheckboxModel,
|
DynamicCheckboxModel,
|
||||||
DynamicFormControlModel,
|
DynamicFormControlModel,
|
||||||
@@ -8,7 +8,7 @@ import {
|
|||||||
} from '@ng-dynamic-forms/core';
|
} from '@ng-dynamic-forms/core';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { combineLatest as observableCombineLatest, Observable, of as observableOf, Subscription } from 'rxjs';
|
import { combineLatest as observableCombineLatest, Observable, of as observableOf, Subscription } from 'rxjs';
|
||||||
import { switchMap, take } from 'rxjs/operators';
|
import { debounceTime, switchMap, take } from 'rxjs/operators';
|
||||||
import { PaginatedList } from '../../../core/data/paginated-list.model';
|
import { PaginatedList } from '../../../core/data/paginated-list.model';
|
||||||
import { RemoteData } from '../../../core/data/remote-data';
|
import { RemoteData } from '../../../core/data/remote-data';
|
||||||
import { EPersonDataService } from '../../../core/eperson/eperson-data.service';
|
import { EPersonDataService } from '../../../core/eperson/eperson-data.service';
|
||||||
@@ -37,7 +37,7 @@ import { ValidateEmailNotTaken } from './validators/email-taken.validator';
|
|||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-eperson-form',
|
selector: 'ds-eperson-form',
|
||||||
templateUrl: './eperson-form.component.html'
|
templateUrl: './eperson-form.component.html',
|
||||||
})
|
})
|
||||||
/**
|
/**
|
||||||
* A form used for creating and editing EPeople
|
* A form used for creating and editing EPeople
|
||||||
@@ -162,7 +162,13 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
|
|||||||
*/
|
*/
|
||||||
isImpersonated = false;
|
isImpersonated = false;
|
||||||
|
|
||||||
constructor(public epersonService: EPersonDataService,
|
/**
|
||||||
|
* Subscription to email field value change
|
||||||
|
*/
|
||||||
|
emailValueChangeSubscribe: Subscription;
|
||||||
|
|
||||||
|
constructor(protected changeDetectorRef: ChangeDetectorRef,
|
||||||
|
public epersonService: EPersonDataService,
|
||||||
public groupsDataService: GroupDataService,
|
public groupsDataService: GroupDataService,
|
||||||
private formBuilderService: FormBuilderService,
|
private formBuilderService: FormBuilderService,
|
||||||
private translateService: TranslateService,
|
private translateService: TranslateService,
|
||||||
@@ -225,8 +231,8 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
|
|||||||
},
|
},
|
||||||
required: true,
|
required: true,
|
||||||
errorMessages: {
|
errorMessages: {
|
||||||
emailTaken: 'error.validation.emailTaken',
|
emailTaken: 'error.validation.emailTaken',
|
||||||
pattern: 'error.validation.NotValidEmail'
|
pattern: 'error.validation.NotValidEmail'
|
||||||
},
|
},
|
||||||
hint: emailHint
|
hint: emailHint
|
||||||
});
|
});
|
||||||
@@ -266,15 +272,18 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
|
|||||||
canLogIn: eperson != null ? eperson.canLogIn : true,
|
canLogIn: eperson != null ? eperson.canLogIn : true,
|
||||||
requireCertificate: eperson != null ? eperson.requireCertificate : false
|
requireCertificate: eperson != null ? eperson.requireCertificate : false
|
||||||
});
|
});
|
||||||
}));
|
|
||||||
|
|
||||||
if (!!this.formGroup.controls.email) {
|
if (eperson === null && !!this.formGroup.controls.email) {
|
||||||
this.formGroup.controls.email.setAsyncValidators(ValidateEmailNotTaken.createValidator(this.epersonService));
|
this.formGroup.controls.email.setAsyncValidators(ValidateEmailNotTaken.createValidator(this.epersonService));
|
||||||
}
|
this.emailValueChangeSubscribe = this.email.valueChanges.pipe(debounceTime(300)).subscribe(() => {
|
||||||
|
this.changeDetectorRef.detectChanges();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
const activeEPerson$ = this.epersonService.getActiveEPerson();
|
const activeEPerson$ = this.epersonService.getActiveEPerson();
|
||||||
|
|
||||||
this.groups = activeEPerson$.pipe(
|
this.groups = activeEPerson$.pipe(
|
||||||
switchMap((eperson) => {
|
switchMap((eperson) => {
|
||||||
return observableCombineLatest([observableOf(eperson), this.paginationService.getFindListOptions(this.config.id, {
|
return observableCombineLatest([observableOf(eperson), this.paginationService.getFindListOptions(this.config.id, {
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
@@ -353,10 +362,10 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
|
|||||||
getFirstCompletedRemoteData()
|
getFirstCompletedRemoteData()
|
||||||
).subscribe((rd: RemoteData<EPerson>) => {
|
).subscribe((rd: RemoteData<EPerson>) => {
|
||||||
if (rd.hasSucceeded) {
|
if (rd.hasSucceeded) {
|
||||||
this.notificationsService.success(this.translateService.get(this.labelPrefix + 'notification.created.success', {name: ePersonToCreate.name}));
|
this.notificationsService.success(this.translateService.get(this.labelPrefix + 'notification.created.success', { name: ePersonToCreate.name }));
|
||||||
this.submitForm.emit(ePersonToCreate);
|
this.submitForm.emit(ePersonToCreate);
|
||||||
} else {
|
} else {
|
||||||
this.notificationsService.error(this.translateService.get(this.labelPrefix + 'notification.created.failure', {name: ePersonToCreate.name}));
|
this.notificationsService.error(this.translateService.get(this.labelPrefix + 'notification.created.failure', { name: ePersonToCreate.name }));
|
||||||
this.cancelForm.emit();
|
this.cancelForm.emit();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -392,10 +401,10 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
|
|||||||
const response = this.epersonService.updateEPerson(editedEperson);
|
const response = this.epersonService.updateEPerson(editedEperson);
|
||||||
response.pipe(getFirstCompletedRemoteData()).subscribe((rd: RemoteData<EPerson>) => {
|
response.pipe(getFirstCompletedRemoteData()).subscribe((rd: RemoteData<EPerson>) => {
|
||||||
if (rd.hasSucceeded) {
|
if (rd.hasSucceeded) {
|
||||||
this.notificationsService.success(this.translateService.get(this.labelPrefix + 'notification.edited.success', {name: editedEperson.name}));
|
this.notificationsService.success(this.translateService.get(this.labelPrefix + 'notification.edited.success', { name: editedEperson.name }));
|
||||||
this.submitForm.emit(editedEperson);
|
this.submitForm.emit(editedEperson);
|
||||||
} else {
|
} else {
|
||||||
this.notificationsService.error(this.translateService.get(this.labelPrefix + 'notification.edited.failure', {name: editedEperson.name}));
|
this.notificationsService.error(this.translateService.get(this.labelPrefix + 'notification.edited.failure', { name: editedEperson.name }));
|
||||||
this.cancelForm.emit();
|
this.cancelForm.emit();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -405,6 +414,87 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Event triggered when the user changes page
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
|
onPageChange(event) {
|
||||||
|
this.updateGroups({
|
||||||
|
currentPage: event,
|
||||||
|
elementsPerPage: this.config.pageSize
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start impersonating the EPerson
|
||||||
|
*/
|
||||||
|
impersonate() {
|
||||||
|
this.authService.impersonate(this.epersonInitial.id);
|
||||||
|
this.isImpersonated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes the EPerson from the Repository. The EPerson will be the only that this form is showing.
|
||||||
|
* It'll either show a success or error message depending on whether the delete was successful or not.
|
||||||
|
*/
|
||||||
|
delete() {
|
||||||
|
this.epersonService.getActiveEPerson().pipe(take(1)).subscribe((eperson: EPerson) => {
|
||||||
|
const modalRef = this.modalService.open(ConfirmationModalComponent);
|
||||||
|
modalRef.componentInstance.dso = eperson;
|
||||||
|
modalRef.componentInstance.headerLabel = 'confirmation-modal.delete-eperson.header';
|
||||||
|
modalRef.componentInstance.infoLabel = 'confirmation-modal.delete-eperson.info';
|
||||||
|
modalRef.componentInstance.cancelLabel = 'confirmation-modal.delete-eperson.cancel';
|
||||||
|
modalRef.componentInstance.confirmLabel = 'confirmation-modal.delete-eperson.confirm';
|
||||||
|
modalRef.componentInstance.brandColor = 'danger';
|
||||||
|
modalRef.componentInstance.confirmIcon = 'fas fa-trash';
|
||||||
|
modalRef.componentInstance.response.pipe(take(1)).subscribe((confirm: boolean) => {
|
||||||
|
if (confirm) {
|
||||||
|
if (hasValue(eperson.id)) {
|
||||||
|
this.epersonService.deleteEPerson(eperson).pipe(getFirstCompletedRemoteData()).subscribe((restResponse: RemoteData<NoContent>) => {
|
||||||
|
if (restResponse.hasSucceeded) {
|
||||||
|
this.notificationsService.success(this.translateService.get(this.labelPrefix + 'notification.deleted.success', { name: eperson.name }));
|
||||||
|
this.submitForm.emit();
|
||||||
|
} else {
|
||||||
|
this.notificationsService.error('Error occured when trying to delete EPerson with id: ' + eperson.id + ' with code: ' + restResponse.statusCode + ' and message: ' + restResponse.errorMessage);
|
||||||
|
}
|
||||||
|
this.cancelForm.emit();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop impersonating the EPerson
|
||||||
|
*/
|
||||||
|
stopImpersonating() {
|
||||||
|
this.authService.stopImpersonatingAndRefresh();
|
||||||
|
this.isImpersonated = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancel the current edit when component is destroyed & unsub all subscriptions
|
||||||
|
*/
|
||||||
|
ngOnDestroy(): void {
|
||||||
|
this.onCancel();
|
||||||
|
this.subs.filter((sub) => hasValue(sub)).forEach((sub) => sub.unsubscribe());
|
||||||
|
this.paginationService.clearPagination(this.config.id);
|
||||||
|
if (hasValue(this.emailValueChangeSubscribe)) {
|
||||||
|
this.emailValueChangeSubscribe.unsubscribe();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method will ensure that the page gets reset and that the cache is cleared
|
||||||
|
*/
|
||||||
|
reset() {
|
||||||
|
this.epersonService.getActiveEPerson().pipe(take(1)).subscribe((eperson: EPerson) => {
|
||||||
|
this.requestService.removeByHrefSubstring(eperson.self);
|
||||||
|
});
|
||||||
|
this.initialisePage();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks for the given ePerson if there is already an ePerson in the system with that email
|
* Checks for the given ePerson if there is already an ePerson in the system with that email
|
||||||
* and shows notification if this is the case
|
* and shows notification if this is the case
|
||||||
@@ -427,17 +517,6 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Event triggered when the user changes page
|
|
||||||
* @param event
|
|
||||||
*/
|
|
||||||
onPageChange(event) {
|
|
||||||
this.updateGroups({
|
|
||||||
currentPage: event,
|
|
||||||
elementsPerPage: this.config.pageSize
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update the list of groups by fetching it from the rest api or cache
|
* Update the list of groups by fetching it from the rest api or cache
|
||||||
*/
|
*/
|
||||||
@@ -446,71 +525,4 @@ export class EPersonFormComponent implements OnInit, OnDestroy {
|
|||||||
this.groups = this.groupsDataService.findAllByHref(eperson._links.groups.href, options);
|
this.groups = this.groupsDataService.findAllByHref(eperson._links.groups.href, options);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Start impersonating the EPerson
|
|
||||||
*/
|
|
||||||
impersonate() {
|
|
||||||
this.authService.impersonate(this.epersonInitial.id);
|
|
||||||
this.isImpersonated = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deletes the EPerson from the Repository. The EPerson will be the only that this form is showing.
|
|
||||||
* It'll either show a success or error message depending on whether the delete was successful or not.
|
|
||||||
*/
|
|
||||||
delete() {
|
|
||||||
this.epersonService.getActiveEPerson().pipe(take(1)).subscribe((eperson: EPerson) => {
|
|
||||||
const modalRef = this.modalService.open(ConfirmationModalComponent);
|
|
||||||
modalRef.componentInstance.dso = eperson;
|
|
||||||
modalRef.componentInstance.headerLabel = 'confirmation-modal.delete-eperson.header';
|
|
||||||
modalRef.componentInstance.infoLabel = 'confirmation-modal.delete-eperson.info';
|
|
||||||
modalRef.componentInstance.cancelLabel = 'confirmation-modal.delete-eperson.cancel';
|
|
||||||
modalRef.componentInstance.confirmLabel = 'confirmation-modal.delete-eperson.confirm';
|
|
||||||
modalRef.componentInstance.brandColor = 'danger';
|
|
||||||
modalRef.componentInstance.confirmIcon = 'fas fa-trash';
|
|
||||||
modalRef.componentInstance.response.pipe(take(1)).subscribe((confirm: boolean) => {
|
|
||||||
if (confirm) {
|
|
||||||
if (hasValue(eperson.id)) {
|
|
||||||
this.epersonService.deleteEPerson(eperson).pipe(getFirstCompletedRemoteData()).subscribe((restResponse: RemoteData<NoContent>) => {
|
|
||||||
if (restResponse.hasSucceeded) {
|
|
||||||
this.notificationsService.success(this.translateService.get(this.labelPrefix + 'notification.deleted.success', { name: eperson.name }));
|
|
||||||
this.submitForm.emit();
|
|
||||||
} else {
|
|
||||||
this.notificationsService.error('Error occured when trying to delete EPerson with id: ' + eperson.id + ' with code: ' + restResponse.statusCode + ' and message: ' + restResponse.errorMessage);
|
|
||||||
}
|
|
||||||
this.cancelForm.emit();
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stop impersonating the EPerson
|
|
||||||
*/
|
|
||||||
stopImpersonating() {
|
|
||||||
this.authService.stopImpersonatingAndRefresh();
|
|
||||||
this.isImpersonated = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cancel the current edit when component is destroyed & unsub all subscriptions
|
|
||||||
*/
|
|
||||||
ngOnDestroy(): void {
|
|
||||||
this.onCancel();
|
|
||||||
this.subs.filter((sub) => hasValue(sub)).forEach((sub) => sub.unsubscribe());
|
|
||||||
this.paginationService.clearPagination(this.config.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method will ensure that the page gets reset and that the cache is cleared
|
|
||||||
*/
|
|
||||||
reset() {
|
|
||||||
this.epersonService.getActiveEPerson().pipe(take(1)).subscribe((eperson: EPerson) => {
|
|
||||||
this.requestService.removeByHrefSubstring(eperson.self);
|
|
||||||
});
|
|
||||||
this.initialisePage();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -11,13 +11,15 @@ export class ValidateEmailNotTaken {
|
|||||||
* This method will create the validator with the ePersonDataService requested from component
|
* This method will create the validator with the ePersonDataService requested from component
|
||||||
* @param ePersonDataService the service with DI in the component that this validator is being utilized.
|
* @param ePersonDataService the service with DI in the component that this validator is being utilized.
|
||||||
*/
|
*/
|
||||||
static createValidator(ePersonDataService: EPersonDataService) {
|
static createValidator(ePersonDataService: EPersonDataService) {
|
||||||
return (control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> => {
|
return (control: AbstractControl): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> => {
|
||||||
return ePersonDataService.getEPersonByEmail(control.value)
|
return ePersonDataService.getEPersonByEmail(control.value)
|
||||||
.pipe(
|
.pipe(
|
||||||
getFirstSucceededRemoteData(),
|
getFirstSucceededRemoteData(),
|
||||||
map(res => { return !!res.payload ? { emailTaken: true } : null; })
|
map(res => {
|
||||||
);
|
return !!res.payload ? { emailTaken: true } : null;
|
||||||
};
|
})
|
||||||
}
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -7,7 +7,11 @@ import { EffectsModule } from '@ngrx/effects';
|
|||||||
import { RouterStateSerializer, StoreRouterConnectingModule } from '@ngrx/router-store';
|
import { RouterStateSerializer, StoreRouterConnectingModule } from '@ngrx/router-store';
|
||||||
import { MetaReducer, Store, StoreModule, USER_PROVIDED_META_REDUCERS } from '@ngrx/store';
|
import { MetaReducer, Store, StoreModule, USER_PROVIDED_META_REDUCERS } from '@ngrx/store';
|
||||||
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
|
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
|
||||||
import { DYNAMIC_MATCHER_PROVIDERS } from '@ng-dynamic-forms/core';
|
import {
|
||||||
|
DYNAMIC_ERROR_MESSAGES_MATCHER,
|
||||||
|
DYNAMIC_MATCHER_PROVIDERS,
|
||||||
|
DynamicErrorMessagesMatcher
|
||||||
|
} from '@ng-dynamic-forms/core';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { ScrollToModule } from '@nicky-lenaers/ngx-scroll-to';
|
import { ScrollToModule } from '@nicky-lenaers/ngx-scroll-to';
|
||||||
|
|
||||||
@@ -52,6 +56,7 @@ import { IdleModalComponent } from './shared/idle-modal/idle-modal.component';
|
|||||||
|
|
||||||
import { UUIDService } from './core/shared/uuid.service';
|
import { UUIDService } from './core/shared/uuid.service';
|
||||||
import { CookieService } from './core/services/cookie.service';
|
import { CookieService } from './core/services/cookie.service';
|
||||||
|
import { AbstractControl } from '@angular/forms';
|
||||||
|
|
||||||
export function getBase() {
|
export function getBase() {
|
||||||
return environment.ui.nameSpace;
|
return environment.ui.nameSpace;
|
||||||
@@ -61,6 +66,14 @@ export function getMetaReducers(): MetaReducer<AppState>[] {
|
|||||||
return environment.debug ? [...appMetaReducers, ...debugMetaReducers] : appMetaReducers;
|
return environment.debug ? [...appMetaReducers, ...debugMetaReducers] : appMetaReducers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Condition for displaying error messages on email form field
|
||||||
|
*/
|
||||||
|
export const ValidateEmailErrorStateMatcher: DynamicErrorMessagesMatcher =
|
||||||
|
(control: AbstractControl, model: any, hasFocus: boolean) => {
|
||||||
|
return (control.touched && !hasFocus) || (control.errors?.emailTaken && hasFocus);
|
||||||
|
};
|
||||||
|
|
||||||
const IMPORTS = [
|
const IMPORTS = [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
SharedModule,
|
SharedModule,
|
||||||
@@ -146,6 +159,10 @@ const PROVIDERS = [
|
|||||||
multi: true,
|
multi: true,
|
||||||
deps: [ CookieService, UUIDService ]
|
deps: [ CookieService, UUIDService ]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: DYNAMIC_ERROR_MESSAGES_MATCHER,
|
||||||
|
useValue: ValidateEmailErrorStateMatcher
|
||||||
|
},
|
||||||
...DYNAMIC_MATCHER_PROVIDERS,
|
...DYNAMIC_MATCHER_PROVIDERS,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user