Skip to content

Commit 377de2c

Browse files
author
Oliver Strik
committed
refactor: alter to match usage described by @jerbob, and extract error logic into own package
1 parent 80788df commit 377de2c

File tree

6 files changed

+100
-33
lines changed

6 files changed

+100
-33
lines changed

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,6 @@ Thumbs.db
5555
# direnv / nix
5656
.direnv/*
5757
.envrc
58+
59+
# @olistrik
60+
.vimignore

projects/demo/src/app/on-submit-errors/on-submit-errors.component.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@
55
<klp-sub-form [injectInto]="form" at="nested">
66
<app-sub-form-example></app-sub-form-example>
77
</klp-sub-form>
8-
<klp-form-submit-button [submitCallback]="onSubmit">submit</klp-form-submit-button>
8+
<klp-form-submit-button [before]="beforeSubmit" [submitCallback]="onSubmit">submit</klp-form-submit-button>
99
</klp-form>
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,50 @@
11
import {Component, inject } from '@angular/core';
22
import { FormBuilder, Validators } from '@angular/forms';
3+
import { FormErrorHandler, FormValidationError, FormValidationErrors, KLP_FORM_ERROR_HANDLER } from '@klippa/ngx-enhancy-forms';
4+
5+
// this is a little like an error from GQL.
6+
type LocationErrors = {
7+
kind: 'LOCATION_ERRORS',
8+
errors: Array<{
9+
message: any,
10+
location: Array<string>;
11+
}>;
12+
};
13+
14+
async function someApiCall(value: {name: string, nested: { name: string }}) {
15+
const locationErrors: LocationErrors = {
16+
kind: 'LOCATION_ERRORS',
17+
errors: [],
18+
};
19+
if (value.name !== 'bob') {
20+
locationErrors.errors.push({location: ['name'], message: 'must be bob'});
21+
}
22+
23+
if (value.nested.name === 'fred') {
24+
locationErrors.errors.push({location: ['nested', 'name'], message: 'cannot be fred'});
25+
}
26+
27+
throw locationErrors;
28+
}
29+
30+
const LocationErrorHandler: FormErrorHandler = (error: any): FormValidationErrors => {
31+
if (error?.kind === 'LOCATION_ERRORS') {
32+
return (error as LocationErrors).errors.map(e => new FormValidationError(
33+
e.location.join('.'),
34+
e.message,
35+
));
36+
}
37+
38+
throw error;
39+
};
340

441
@Component({
542
selector: 'app-on-submit-errors',
643
templateUrl: './on-submit-errors.component.html',
7-
styleUrls: ['./on-submit-errors.component.scss']
44+
styleUrls: ['./on-submit-errors.component.scss'],
45+
providers: [
46+
{provide: KLP_FORM_ERROR_HANDLER, useValue: LocationErrorHandler },
47+
]
848
})
949
export class OnSubmitErrorsComponent {
1050
private fb = inject(FormBuilder);
@@ -25,22 +65,11 @@ export class OnSubmitErrorsComponent {
2565
);
2666
}
2767

28-
protected onSubmit = async (value: any) => {
29-
const err: any = {};
30-
if (value.name !== 'bob') {
31-
err.name = 'must be bob';
32-
}
33-
34-
if (value.nested.name === 'fred') {
35-
err['nested.name'] = 'cannot be fred';
36-
}
37-
38-
if (value.nested.name !== 'greg') {
39-
err.nested = {
40-
name: 'cannot be fred'
41-
};
42-
}
68+
protected beforeSubmit = async () => {
69+
throw new Error('bar');
70+
}
4371

44-
throw err;
72+
protected onSubmit = async (value: any) => {
73+
await someApiCall(value);
4574
}
4675
}

projects/klippa/ngx-enhancy-forms/src/lib/form/form-submit-button/form-submit-button.component.ts

+13-15
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { Component, Host, HostBinding, Input, Optional } from '@angular/core';
1+
import { Component, HostBinding, inject, Input } from '@angular/core';
22
import {FormComponent, invalidFieldsSymbol} from '../form.component';
3-
import {isNullOrUndefined} from '../../util/values';
43
import { ButtonVariant } from '../../elements/button/button.component';
4+
import { DefaultErrorHandler, FormValidationError, KLP_FORM_ERROR_HANDLER } from '../form-validation-error/form-validation-error';
55

66
export type SubmitButtonVariant = Extract<ButtonVariant,
77
| 'greenFilled'
@@ -16,25 +16,22 @@ export type SubmitButtonVariant = Extract<ButtonVariant,
1616
styleUrls: ['./form-submit-button.component.scss'],
1717
})
1818
export class FormSubmitButtonComponent {
19+
private parentForm = inject(FormComponent, {host: true, optional: true});
20+
private handleError = inject(KLP_FORM_ERROR_HANDLER, {optional: true}) ?? DefaultErrorHandler;
1921

20-
@HostBinding('class._fullWidth') get _() {
21-
return this.fullWidth;
22-
}
23-
24-
constructor(@Host() @Optional() private parentForm: FormComponent) {}
2522
@Input() public isLoading = false;
26-
@Input() fullWidth = false;
27-
@Input() variant: SubmitButtonVariant = 'greenFilled';
23+
@Input() @HostBinding('class._fullWidth') public fullWidth = false;
24+
@Input() public variant: SubmitButtonVariant = 'greenFilled';
2825
@Input() public submitCallback: (renderedAndEnabledValues: object, renderedButDisabledValues: object) => Promise<any>;
2926
@Input() public before: () => Promise<any> = () => Promise.resolve();
3027
@Input() public after: () => Promise<any> = () => Promise.resolve();
3128

29+
private renderError = (e: FormValidationError) => {
30+
this.parentForm.formGroup.get(e.path)?.setErrors({ message: { value: e.message }});
31+
}
32+
3233
async submitForm(): Promise<void> {
33-
try {
34-
await this.before();
35-
} catch (e) {
36-
return;
37-
}
34+
await this.before().catch(() => null);
3835

3936
try {
4037
const [renderedAndEnabledValues, renderedValues] = await this.parentForm.trySubmit();
@@ -45,7 +42,8 @@ export class FormSubmitButtonComponent {
4542
if (e === invalidFieldsSymbol) {
4643
return;
4744
}
48-
throw e;
45+
this.handleError(e).forEach(this.renderError);
46+
return;
4947
}
5048

5149
await this.after();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { InjectionToken } from '@angular/core';
2+
3+
export class FormValidationError extends Error {
4+
_path: string;
5+
_message: string;
6+
7+
constructor(path: string, message: string) {
8+
super(message);
9+
this.name = 'FORM_VALIDATION_ERROR';
10+
this._path = path;
11+
this._message = message;
12+
}
13+
14+
get path() {
15+
return this._path;
16+
}
17+
}
18+
19+
export type FormValidationErrors = Array<FormValidationError>;
20+
21+
export type FormErrorHandler = (error: any) => FormValidationErrors;
22+
23+
export const KLP_FORM_ERROR_HANDLER = new InjectionToken<FormErrorHandler>('KLP_FORM_ERROR_HANDLER');
24+
25+
export const DefaultErrorHandler: FormErrorHandler = (error: any) => {
26+
if (Array.isArray(error) && error.reduce((acc, err) => acc && err instanceof FormValidationError)) {
27+
// If the error is an array of FormValidationErrors, then pass it along.
28+
return error;
29+
} else if (error instanceof FormValidationError) {
30+
// If the error is a FormValidationError, then wrap it and pass it on.
31+
return [error];
32+
}
33+
34+
throw error;
35+
};
36+

projects/klippa/ngx-enhancy-forms/src/public-api.ts

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export * from './lib/form/form-caption/form-caption.component';
3131
export * from './lib/form/form-element/form-element.component';
3232
export * from './lib/form/form-error/form-error.component';
3333
export * from './lib/form/form-submit-button/form-submit-button.component';
34+
export * from './lib/form/form-validation-error/form-validation-error';
3435

3536
export * from './lib/validators/dateValidator';
3637

0 commit comments

Comments
 (0)