Skip to content

Commit

Permalink
fix: show error on async statusChanges after submit
Browse files Browse the repository at this point in the history
If you use control.setErrors(...) in response to an async result, the valueChanges method will not fire until the next blur/submit event.

This fix includes statusChanges events after the form has been submitted.
  • Loading branch information
rhutchison committed Jun 17, 2020
1 parent 68970c7 commit ef9f00e
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Component, Type } from '@angular/core';
import { FormArray, FormBuilder, FormControl, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms';
import { byPlaceholder, byText, createComponentFactory, Spectator } from '@ngneat/spectator';
import { ErrorTailorModule } from '@ngneat/error-tailor';
import { tick, fakeAsync } from '@angular/core/testing';

function getComponentFactory<C>(component: Type<C>) {
return createComponentFactory({
Expand All @@ -14,7 +15,8 @@ function getComponentFactory<C>(component: Type<C>) {
useValue: {
required: () => 'required error',
minlength: () => 'min error',
requiredone: () => 'required one error'
requiredone: () => 'required one error',
serverError: error => error
}
}
})
Expand Down Expand Up @@ -101,6 +103,29 @@ describe('ControlErrorDirective', () => {

expect(spectator.query(byText(/error/))).toBeNull();
});

it('should show errors on async statusChanges', fakeAsync(() => {
const serverError = 'server error';
const nameInput = spectator.query<HTMLInputElement>(byPlaceholder('Name'));

typeInElementAndFocusOut(spectator, 'no error', nameInput);

expect(spectator.query(byText(serverError))).toBeFalsy();

spectator.click('button');

setTimeout(() => {
const control = spectator.component.form.get('name');

control.setErrors({ serverError });
}, 50);

tick(50);

spectator.detectChanges();

expect(spectator.query(byText(serverError))).toBeTruthy();
}));
});

describe('FormControl', () => {
Expand Down
12 changes: 7 additions & 5 deletions projects/ngneat/error-tailor/src/lib/control-error.directive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { ControlErrorComponent } from './control-error.component';
import { ControlErrorAnchorDirective } from './control-error-anchor.directive';
import { EMPTY, fromEvent, merge, Observable, Subject } from 'rxjs';
import { ErrorTailorConfig, ErrorTailorConfigProvider, FORM_ERRORS } from './providers';
import { startWith, switchMap, takeUntil } from 'rxjs/operators';
import { distinctUntilChanged, startWith, switchMap, takeUntil } from 'rxjs/operators';
import { FormSubmitDirective } from './form-submit.directive';
import { ErrorsMap } from './types';

Expand Down Expand Up @@ -58,19 +58,21 @@ export class ControlErrorsDirective implements OnInit, OnDestroy {
this.control = (this.controlContainer || this.ngControl).control;
const isInput = this.mergedConfig.blurPredicate(this.host.nativeElement);

const statusChanges$ = this.control.statusChanges.pipe(distinctUntilChanged());
const valueChanges$ = this.control.valueChanges;
let changes$: Observable<any> = EMPTY;
const controlChanges$ = merge(statusChanges$, valueChanges$);
let changesOnBlur$: Observable<any> = EMPTY;

if (this.controlErrorsOnBlur && isInput) {
const blur$ = fromEvent(this.host.nativeElement, 'focusout');
// blurFirstThanUponChange
changes$ = blur$.pipe(switchMap(() => valueChanges$.pipe(startWith(true))));
changesOnBlur$ = blur$.pipe(switchMap(() => valueChanges$.pipe(startWith(true))));
}

// submitFirstThanUponChanges
const submit$ = this.submit$.pipe(switchMap(() => valueChanges$.pipe(startWith(true))));
const changesOnSubmit$ = this.submit$.pipe(switchMap(() => controlChanges$.pipe(startWith(true))));

merge(submit$, changes$)
merge(changesOnSubmit$, changesOnBlur$)
.pipe(takeUntil(this.destroy))
.subscribe(() => this.valueChanges());
}
Expand Down

0 comments on commit ef9f00e

Please # to comment.