I cloned the Angular seed project and have implemented a suitable schema/uischema and some mock data.
However I’m struggling to figure out how to wire up a custom text input validator that must call a backend service to validate its input and display an error if invalid.
I tried creating a custom component by copying TextControlRenderer and adding the relevant parts from custom.autocomplete.ts, but there are several problems:
-
this.form.valueChanges
seems to generate events when editing all other inputs than the custom component (e.g. a TextControlRenderer) -
I cannot seem to reliably add an error to the component when the service call has been resolved and the result is an error. Sometimes the component is marked invalid (red underline) and sometimes not. Also the spinner is not showing up, or sometimes showing up and not disappearing.
What am I doing wrong?
Disclaimer: I’m pretty rusty with web dev
Thanks
My component:
import {ChangeDetectionStrategy, Component} from '@angular/core';
import {JsonFormsAngularService, JsonFormsControl} from '@jsonforms/angular';
import {ValidationService} from "./validation/validation.service";
import {debounceTime, finalize, switchMap, tap} from "rxjs/operators";
@Component({
selector: 'MyTextControlRenderer',
template: `
<mat-form-field fxFlex [fxHide]="hidden">
<mat-label>{{ label }}</mat-label>
<input
style="background-color: aquamarine"
matInput
[type]="getType()"
(input)="onChange($event)"
[id]="id"
[formControl]="form"
/>
<mat-option *ngIf="isLoading" class="is-loading">
<mat-spinner diameter="30"></mat-spinner>
</mat-option>
<mat-hint *ngIf="shouldShowUnfocusedDescription()">{{ description }}</mat-hint>
<mat-error>{{ error }}</mat-error>
</mat-form-field>
`,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class MyTextControlRenderer extends JsonFormsControl {
isLoading: boolean;
constructor(jsonformsService: JsonFormsAngularService, private validationService: ValidationService) {
super(jsonformsService);
}
ngOnInit() {
super.ngOnInit();
console.log(JSON.stringify(this.form));
this.form.valueChanges
.pipe(
tap(() => console.log("onChange")),
debounceTime(300),
tap(() => this.isLoading = true),
switchMap(value => this.validationService.validateCareTeam(value)
.pipe(
finalize(() => {
console.log("finalize")
return this.isLoading = false;
})
)
)
)
.subscribe(isValid => {
console.log("next value: " + isValid)
this.form.setErrors({invalid: true});
//if (!isValid) this.error = "invalid"; <-- not working either
});
}
getEventValue = (event: any) => event.target.value;
getType = (): string => {
if (this.uischema.options && this.uischema.options.format) {
return this.uischema.options.format;
}
if (this.scopedSchema && this.scopedSchema.format) {
switch (this.scopedSchema.format) {
case 'email':
return 'email';
case 'tel':
return 'tel';
default:
return 'text';
}
}
return 'text';
};
}
My service class:
export class ValidationService {
constructor(private http: HttpClient) { }
validateCareTeam(ref: String): Observable<boolean> {
// return this.http.get<boolean>('./asdf');
console.log("service called")
return of(false).pipe(delay(1000));
}
}
[original thread by Tue Toft Nørgård]