first-npm-package-nicule
Version:
This isi first npm package
127 lines (107 loc) • 4.54 kB
text/typescript
import {catchError, switchMap, filter, tap, map} from 'rxjs/operators';
import { AfterViewInit, Directive, EventEmitter, Host, Input, OnDestroy, Output } from '@angular/core';
import { FormControl, FormGroup, NgForm } from '@angular/forms';
import { Subscription } from 'rxjs';
import { ActionExecutor, Hypermedia, HypermediaAction } from 'first-npm-package-nicule/core';
export function actionNameFactory(ngForm: NgForm): string {
return ngForm.name;
}
export class FormDirective implements OnDestroy {
private submitSubscription: Subscription;
private action: HypermediaAction;
submitSuccess = new EventEmitter();
submitError = new EventEmitter();
submitComplete = new EventEmitter();
set hmForm(action: HypermediaAction | any) {
if (action) {
this.action = action;
this.ngForm.name = action.name;
this.subscribeToFormSubmit();
}
}
tokenOverride;
ngOnDestroy(): void {
if (this.submitSubscription) {
this.submitSubscription.unsubscribe();
}
}
constructor( private ngForm: NgForm, private actionExecutor: ActionExecutor) { }
private subscribeToFormSubmit(): void {
if (this.submitSubscription) {
this.submitSubscription.unsubscribe();
}
this.submitSubscription =
this.ngForm
.ngSubmit.pipe(
map(event => event as Event),
tap(event => event.preventDefault()),
filter(_ => {
if (!this.isFormValid()) {
this.submitComplete.emit();
}
return this.isFormValid();
}),
switchMap(event => {
return this.tokenOverride ? this.actionExecutor.execute(this.action, this.ngForm.value, true, this.tokenOverride) : this.actionExecutor.execute(this.action, this.ngForm.value);
}),
catchError((err, caught) => {
this.submitError.emit(err);
this.submitComplete.emit();
return [];
}),)
.subscribe({ next: this.onResponse.bind(this) });
}
private isFormValid(): boolean {
this.validateForm(this.ngForm.control);
return this.ngForm.valid || Object.keys(this.ngForm.controls).length === 0;
}
validateForm(formGroup: FormGroup): void {
Object.keys(formGroup.controls)
.forEach(field => {
const control = formGroup.get(field);
if (control instanceof FormControl) {
control.markAsTouched({ onlySelf: true });
control.markAsDirty({ onlySelf: true });
} else if (control instanceof FormGroup) {
this.validateForm(control);
}
});
}
computeClassifiedErrors(messages: Array<string>): any {
return messages.reduce((acc, message) => ({ ...acc, [message.slice(message.lastIndexOf('.') + 1)]: message }), {});
}
private onResponse(hypermedia: Hypermedia): void {
if (!hypermedia) {
this.submitError.emit();
this.submitComplete.emit();
return;
}
const { class: classes, properties } = hypermedia;
if (classes && classes.includes('error')) {
if (properties.validationErrors) {
const validationErrors = properties.validationErrors;
validationErrors.forEach(
validationError =>
this.ngForm
.form
.controls[validationError.name]
.setErrors(
this.computeClassifiedErrors(validationError.msg)));
this.submitError.emit(validationErrors);
}
if (properties.msg) {
this.submitError.emit(properties.msg);
}
if (classes.includes('service-unavailable')) {
this.submitError.emit({ errorType: 'service-unavailable' });
}
} else {
this.submitSuccess.emit(hypermedia);
}
this.submitComplete.emit();
}
}