Skip to content

Commit 84841d0

Browse files
committed
Address comments
1 parent 3ff8d87 commit 84841d0

File tree

5 files changed

+61
-40
lines changed

5 files changed

+61
-40
lines changed

src/lib/core/core.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ export {
120120

121121
// Error
122122
export {
123-
ErrorStateMatcherType,
123+
ErrorStateMatcher,
124124
ErrorOptions,
125125
MD_ERROR_GLOBAL_OPTIONS
126126
} from './error/error-options';

src/lib/core/error/error-options.ts

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,45 @@
1+
/**
2+
* @license
3+
* Copyright Google Inc. All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
19
import {InjectionToken} from '@angular/core';
210
import {NgControl, FormGroupDirective, NgForm} from '@angular/forms';
311

412
/** Injection token that can be used to specify the global error options. */
513
export const MD_ERROR_GLOBAL_OPTIONS =
6-
new InjectionToken<() => boolean>('md-error-global-options');
14+
new InjectionToken<ErrorOptions>('md-error-global-options');
715

8-
export type ErrorStateMatcherType =
16+
export type ErrorStateMatcher =
917
(control: NgControl, parentFormGroup: FormGroupDirective, parentForm: NgForm) => boolean;
1018

1119
export interface ErrorOptions {
12-
errorStateMatcher?: ErrorStateMatcherType;
13-
showOnDirty?: boolean;
20+
errorStateMatcher?: ErrorStateMatcher;
21+
}
22+
23+
export class DefaultErrorStateMatcher {
24+
25+
errorStateMatcher(control: NgControl, formGroup: FormGroupDirective, form: NgForm): boolean {
26+
const isInvalid = control && control.invalid;
27+
const isTouched = control && control.touched;
28+
const isSubmitted = (formGroup && formGroup.submitted) ||
29+
(form && form.submitted);
30+
31+
return !!(isInvalid && (isTouched || isSubmitted));
32+
}
33+
}
34+
35+
export class ShowOnDirtyErrorStateMatcher {
36+
37+
errorStateMatcher(control: NgControl, formGroup: FormGroupDirective, form: NgForm): boolean {
38+
const isInvalid = control && control.invalid;
39+
const isDirty = control && control.dirty;
40+
const isSubmitted = (formGroup && formGroup.submitted) ||
41+
(form && form.submitted);
42+
43+
return !!(isInvalid && (isDirty || isSubmitted));
44+
}
1445
}

src/lib/input/input-container.spec.ts

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import {
2424
getMdInputContainerPlaceholderConflictError
2525
} from './input-container-errors';
2626
import {MD_PLACEHOLDER_GLOBAL_OPTIONS} from '../core/placeholder/placeholder-options';
27-
import {MD_ERROR_GLOBAL_OPTIONS} from '../core/error/error-options';
27+
import {MD_ERROR_GLOBAL_OPTIONS, ShowOnDirtyErrorStateMatcher} from '../core/error/error-options';
2828

2929
describe('MdInputContainer', function () {
3030
beforeEach(async(() => {
@@ -676,8 +676,6 @@ describe('MdInputContainer', function () {
676676
}));
677677

678678
it('should display an error message when the parent form group is submitted', async(() => {
679-
fixture.destroy();
680-
681679
let groupFixture = TestBed.createComponent(MdInputContainerWithFormGroupErrorMessages);
682680
let component: MdInputContainerWithFormGroupErrorMessages;
683681

@@ -709,8 +707,6 @@ describe('MdInputContainer', function () {
709707
}));
710708

711709
it('should display an error message when a custom error matcher returns true', async(() => {
712-
fixture.destroy();
713-
714710
let customFixture = TestBed.createComponent(MdInputContainerWithCustomErrorStateMatcher);
715711
let component: MdInputContainerWithCustomErrorStateMatcher;
716712

@@ -726,14 +722,14 @@ describe('MdInputContainer', function () {
726722

727723
customFixture.whenStable().then(() => {
728724
expect(containerEl.querySelectorAll('md-error').length)
729-
.toBe(0, 'Expected no error messages after being touched.');
725+
.toBe(0, 'Expected no error messages after being touched.');
730726

731727
component.errorState = true;
732728
customFixture.detectChanges();
733729

734730
customFixture.whenStable().then(() => {
735731
expect(containerEl.querySelectorAll('md-error').length)
736-
.toBe(1, 'Expected one error messages to have been rendered.');
732+
.toBe(1, 'Expected one error messages to have been rendered.');
737733
});
738734
});
739735
}));
@@ -774,7 +770,7 @@ describe('MdInputContainer', function () {
774770
expect(containerEl.querySelectorAll('md-error').length).toBe(1, 'Expected an error message');
775771
});
776772

777-
it('should display an error message when global setting shows errors on dirty', async() => {
773+
it('should display an error message when using ShowOnDirtyErrorStateMatcher', async(() => {
778774
TestBed.resetTestingModule();
779775
TestBed.configureTestingModule({
780776
imports: [
@@ -787,7 +783,7 @@ describe('MdInputContainer', function () {
787783
MdInputContainerWithFormErrorMessages
788784
],
789785
providers: [
790-
{ provide: MD_ERROR_GLOBAL_OPTIONS, useValue: { showOnDirty: true } }
786+
{ provide: MD_ERROR_GLOBAL_OPTIONS, useClass: ShowOnDirtyErrorStateMatcher }
791787
]
792788
});
793789

@@ -805,18 +801,18 @@ describe('MdInputContainer', function () {
805801

806802
customFixture.whenStable().then(() => {
807803
expect(containerEl.querySelectorAll('md-error').length)
808-
.toBe(0, 'Expected no error messages when touched');
804+
.toBe(0, 'Expected no error messages when touched');
809805

810806
testComponent.formControl.markAsDirty();
811807
customFixture.detectChanges();
812808

813809
customFixture.whenStable().then(() => {
814810
expect(containerEl.querySelectorAll('md-error').length)
815-
.toBe(1, 'Expected one error message when dirty');
811+
.toBe(1, 'Expected one error message when dirty');
816812
});
817813
});
818814

819-
});
815+
}));
820816

821817
it('should hide the errors and show the hints once the input becomes valid', async(() => {
822818
testComponent.formControl.markAsTouched();

src/lib/input/input-container.ts

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ import {
4343
MD_PLACEHOLDER_GLOBAL_OPTIONS
4444
} from '../core/placeholder/placeholder-options';
4545
import {
46-
ErrorStateMatcherType,
46+
DefaultErrorStateMatcher,
47+
ErrorStateMatcher,
4748
ErrorOptions,
4849
MD_ERROR_GLOBAL_OPTIONS
4950
} from '../core/error/error-options';
@@ -196,7 +197,7 @@ export class MdInputDirective {
196197
}
197198

198199
/** A function used to control when error messages are shown. */
199-
@Input() errorStateMatcher: ErrorStateMatcherType;
200+
@Input() errorStateMatcher: ErrorStateMatcher;
200201

201202
/** The input element's value. */
202203
get value() { return this._elementRef.nativeElement.value; }
@@ -240,7 +241,8 @@ export class MdInputDirective {
240241
this.id = this.id;
241242

242243
this._errorOptions = errorOptions ? errorOptions : {};
243-
this.errorStateMatcher = this._errorOptions.errorStateMatcher || undefined;
244+
this.errorStateMatcher = this._errorOptions.errorStateMatcher
245+
|| new DefaultErrorStateMatcher().errorStateMatcher;
244246
}
245247

246248
/** Focuses the input element. */
@@ -263,24 +265,7 @@ export class MdInputDirective {
263265
/** Whether the input is in an error state. */
264266
_isErrorState(): boolean {
265267
const control = this._ngControl;
266-
return this.errorStateMatcher
267-
? this.errorStateMatcher(control, this._parentFormGroup, this._parentForm)
268-
: this._defaultErrorStateMatcher(control);
269-
}
270-
271-
/** Default error state calculation */
272-
private _defaultErrorStateMatcher(control: NgControl): boolean {
273-
const isInvalid = control && control.invalid;
274-
const isTouched = control && control.touched;
275-
const isDirty = control && control.dirty;
276-
const isSubmitted = (this._parentFormGroup && this._parentFormGroup.submitted) ||
277-
(this._parentForm && this._parentForm.submitted);
278-
279-
if (this._errorOptions.showOnDirty) {
280-
return !!(isInvalid && (isDirty || isSubmitted));
281-
}
282-
283-
return !!(isInvalid && (isTouched || isSubmitted));
268+
return this.errorStateMatcher(control, this._parentFormGroup, this._parentForm);
284269
}
285270

286271
/** Make sure the input is a supported type. */

src/lib/input/input.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,17 @@ to all inputs.
142142

143143
Here are the available global options:
144144

145-
146145
| Name | Type | Description |
147146
| ----------------- | -------- | ----------- |
148147
| errorStateMatcher | Function | Returns a boolean specifying if the error should be shown |
149-
| showOnDirty | boolean | If true, the error will show when the control is dirty, not touched. |P
148+
149+
150+
If you just wish to make all inputs behave the same as the default, but show errors when
151+
dirty instead of touched, you can use the `ShowOnDirtyErrorStateMatcher` implementation.
152+
153+
```ts
154+
@NgModule({
155+
providers: [
156+
{ provide: MD_ERROR_GLOBAL_OPTIONS, useClass: ShowOnDirtyErrorStateMatcher }
157+
]
158+
})

0 commit comments

Comments
 (0)