diff options
author | Chocobozzz <me@florianbigard.com> | 2021-12-29 15:33:24 +0100 |
---|---|---|
committer | Chocobozzz <me@florianbigard.com> | 2022-01-03 14:20:52 +0100 |
commit | cc4bf76c13e38e9065d49161b6e0485657424577 (patch) | |
tree | d7ecc6bd58037c41587eb911776b676592985cd1 /client/src/app/shared | |
parent | e2aeb8ad0f3055d54ac416ec5908d26b70aac4be (diff) | |
download | PeerTube-cc4bf76c13e38e9065d49161b6e0485657424577.tar.gz PeerTube-cc4bf76c13e38e9065d49161b6e0485657424577.tar.zst PeerTube-cc4bf76c13e38e9065d49161b6e0485657424577.zip |
Handle async validators
Diffstat (limited to 'client/src/app/shared')
4 files changed, 44 insertions, 18 deletions
diff --git a/client/src/app/shared/form-validators/form-validator.model.ts b/client/src/app/shared/form-validators/form-validator.model.ts index 6f2472ccd..31c253b9b 100644 --- a/client/src/app/shared/form-validators/form-validator.model.ts +++ b/client/src/app/shared/form-validators/form-validator.model.ts | |||
@@ -1,7 +1,9 @@ | |||
1 | import { ValidatorFn } from '@angular/forms' | 1 | import { AsyncValidatorFn, ValidatorFn } from '@angular/forms' |
2 | 2 | ||
3 | export type BuildFormValidator = { | 3 | export type BuildFormValidator = { |
4 | VALIDATORS: ValidatorFn[] | 4 | VALIDATORS: ValidatorFn[] |
5 | ASYNC_VALIDATORS?: AsyncValidatorFn[] | ||
6 | |||
5 | MESSAGES: { [ name: string ]: string } | 7 | MESSAGES: { [ name: string ]: string } |
6 | } | 8 | } |
7 | 9 | ||
diff --git a/client/src/app/shared/shared-forms/form-reactive.ts b/client/src/app/shared/shared-forms/form-reactive.ts index 30b59c141..07a12c6f6 100644 --- a/client/src/app/shared/shared-forms/form-reactive.ts +++ b/client/src/app/shared/shared-forms/form-reactive.ts | |||
@@ -1,4 +1,6 @@ | |||
1 | |||
1 | import { FormGroup } from '@angular/forms' | 2 | import { FormGroup } from '@angular/forms' |
3 | import { wait } from '@root-helpers/utils' | ||
2 | import { BuildFormArgument, BuildFormDefaultValues } from '../form-validators/form-validator.model' | 4 | import { BuildFormArgument, BuildFormDefaultValues } from '../form-validators/form-validator.model' |
3 | import { FormValidatorService } from './form-validator.service' | 5 | import { FormValidatorService } from './form-validator.service' |
4 | 6 | ||
@@ -22,30 +24,42 @@ export abstract class FormReactive { | |||
22 | this.formErrors = formErrors | 24 | this.formErrors = formErrors |
23 | this.validationMessages = validationMessages | 25 | this.validationMessages = validationMessages |
24 | 26 | ||
25 | this.form.valueChanges.subscribe(() => this.onValueChanged(this.form, this.formErrors, this.validationMessages, false)) | 27 | this.form.statusChanges.subscribe(async status => { |
28 | // FIXME: remove when https://github.com/angular/angular/issues/41519 is fixed | ||
29 | await this.waitPendingCheck() | ||
30 | |||
31 | this.onStatusChanged(this.form, this.formErrors, this.validationMessages) | ||
32 | }) | ||
26 | } | 33 | } |
27 | 34 | ||
28 | protected forceCheck () { | 35 | protected async waitPendingCheck () { |
29 | return this.onValueChanged(this.form, this.formErrors, this.validationMessages, true) | 36 | if (this.form.status !== 'PENDING') return |
37 | |||
38 | // FIXME: the following line does not work: https://github.com/angular/angular/issues/41519 | ||
39 | // return firstValueFrom(this.form.statusChanges.pipe(filter(status => status !== 'PENDING'))) | ||
40 | // So we have to fallback to active wait :/ | ||
41 | |||
42 | do { | ||
43 | await wait(10) | ||
44 | } while (this.form.status === 'PENDING') | ||
30 | } | 45 | } |
31 | 46 | ||
32 | protected check () { | 47 | protected forceCheck () { |
33 | return this.onValueChanged(this.form, this.formErrors, this.validationMessages, false) | 48 | this.onStatusChanged(this.form, this.formErrors, this.validationMessages, false) |
34 | } | 49 | } |
35 | 50 | ||
36 | private onValueChanged ( | 51 | private onStatusChanged ( |
37 | form: FormGroup, | 52 | form: FormGroup, |
38 | formErrors: FormReactiveErrors, | 53 | formErrors: FormReactiveErrors, |
39 | validationMessages: FormReactiveValidationMessages, | 54 | validationMessages: FormReactiveValidationMessages, |
40 | forceCheck = false | 55 | onlyDirty = true |
41 | ) { | 56 | ) { |
42 | for (const field of Object.keys(formErrors)) { | 57 | for (const field of Object.keys(formErrors)) { |
43 | if (formErrors[field] && typeof formErrors[field] === 'object') { | 58 | if (formErrors[field] && typeof formErrors[field] === 'object') { |
44 | this.onValueChanged( | 59 | this.onStatusChanged( |
45 | form.controls[field] as FormGroup, | 60 | form.controls[field] as FormGroup, |
46 | formErrors[field] as FormReactiveErrors, | 61 | formErrors[field] as FormReactiveErrors, |
47 | validationMessages[field] as FormReactiveValidationMessages, | 62 | validationMessages[field] as FormReactiveValidationMessages |
48 | forceCheck | ||
49 | ) | 63 | ) |
50 | continue | 64 | continue |
51 | } | 65 | } |
@@ -56,8 +70,7 @@ export abstract class FormReactive { | |||
56 | 70 | ||
57 | if (control.dirty) this.formChanged = true | 71 | if (control.dirty) this.formChanged = true |
58 | 72 | ||
59 | if (forceCheck) control.updateValueAndValidity({ emitEvent: false }) | 73 | if (!control || (onlyDirty && !control.dirty) || !control.enabled || !control.errors) continue |
60 | if (!control || !control.dirty || !control.enabled || control.valid) continue | ||
61 | 74 | ||
62 | const staticMessages = validationMessages[field] | 75 | const staticMessages = validationMessages[field] |
63 | for (const key of Object.keys(control.errors)) { | 76 | for (const key of Object.keys(control.errors)) { |
@@ -65,11 +78,10 @@ export abstract class FormReactive { | |||
65 | 78 | ||
66 | // Try to find error message in static validation messages first | 79 | // Try to find error message in static validation messages first |
67 | // Then check if the validator returns a string that is the error | 80 | // Then check if the validator returns a string that is the error |
68 | if (typeof formErrorValue === 'boolean') formErrors[field] += staticMessages[key] + ' ' | 81 | if (staticMessages[key]) formErrors[field] += staticMessages[key] + ' ' |
69 | else if (typeof formErrorValue === 'string') formErrors[field] += control.errors[key] | 82 | else if (typeof formErrorValue === 'string') formErrors[field] += control.errors[key] |
70 | else throw new Error('Form error value of ' + field + ' is invalid') | 83 | else throw new Error('Form error value of ' + field + ' is invalid') |
71 | } | 84 | } |
72 | } | 85 | } |
73 | } | 86 | } |
74 | |||
75 | } | 87 | } |
diff --git a/client/src/app/shared/shared-forms/form-validator.service.ts b/client/src/app/shared/shared-forms/form-validator.service.ts index 055fbb2d9..0fe50ac9b 100644 --- a/client/src/app/shared/shared-forms/form-validator.service.ts +++ b/client/src/app/shared/shared-forms/form-validator.service.ts | |||
@@ -1,5 +1,5 @@ | |||
1 | import { Injectable } from '@angular/core' | 1 | import { Injectable } from '@angular/core' |
2 | import { FormBuilder, FormControl, FormGroup, ValidatorFn } from '@angular/forms' | 2 | import { AsyncValidatorFn, FormArray, FormBuilder, FormControl, FormGroup, ValidatorFn } from '@angular/forms' |
3 | import { BuildFormArgument, BuildFormDefaultValues } from '../form-validators/form-validator.model' | 3 | import { BuildFormArgument, BuildFormDefaultValues } from '../form-validators/form-validator.model' |
4 | import { FormReactiveErrors, FormReactiveValidationMessages } from './form-reactive' | 4 | import { FormReactiveErrors, FormReactiveValidationMessages } from './form-reactive' |
5 | 5 | ||
@@ -68,11 +68,23 @@ export class FormValidatorService { | |||
68 | 68 | ||
69 | form.addControl( | 69 | form.addControl( |
70 | name, | 70 | name, |
71 | new FormControl(defaultValue, field?.VALIDATORS as ValidatorFn[]) | 71 | new FormControl(defaultValue, field?.VALIDATORS as ValidatorFn[], field?.ASYNC_VALIDATORS as AsyncValidatorFn[]) |
72 | ) | 72 | ) |
73 | } | 73 | } |
74 | } | 74 | } |
75 | 75 | ||
76 | updateTreeValidity (group: FormGroup | FormArray): void { | ||
77 | for (const key of Object.keys(group.controls)) { | ||
78 | const abstractControl = group.controls[key] as FormControl | ||
79 | |||
80 | if (abstractControl instanceof FormGroup || abstractControl instanceof FormArray) { | ||
81 | this.updateTreeValidity(abstractControl) | ||
82 | } else { | ||
83 | abstractControl.updateValueAndValidity({ emitEvent: false }) | ||
84 | } | ||
85 | } | ||
86 | } | ||
87 | |||
76 | private isRecursiveField (field: any) { | 88 | private isRecursiveField (field: any) { |
77 | return field && typeof field === 'object' && !field.MESSAGES && !field.VALIDATORS | 89 | return field && typeof field === 'object' && !field.MESSAGES && !field.VALIDATORS |
78 | } | 90 | } |
diff --git a/client/src/app/shared/shared-user-subscription/remote-subscribe.component.ts b/client/src/app/shared/shared-user-subscription/remote-subscribe.component.ts index a951134eb..369692715 100644 --- a/client/src/app/shared/shared-user-subscription/remote-subscribe.component.ts +++ b/client/src/app/shared/shared-user-subscription/remote-subscribe.component.ts | |||
@@ -27,7 +27,7 @@ export class RemoteSubscribeComponent extends FormReactive implements OnInit { | |||
27 | } | 27 | } |
28 | 28 | ||
29 | onValidKey () { | 29 | onValidKey () { |
30 | this.check() | 30 | this.forceCheck() |
31 | if (!this.form.valid) return | 31 | if (!this.form.valid) return |
32 | 32 | ||
33 | this.formValidated() | 33 | this.formValidated() |