Skip to content

Commit 062ceb9

Browse files
committed
refactor: split error component into module
1 parent 9c78bc2 commit 062ceb9

16 files changed

+100
-70
lines changed

src/lib/core/_core.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
@import 'option/option-theme';
88
@import 'option/optgroup';
99
@import 'option/optgroup-theme';
10+
@import 'error/error';
11+
@import 'error/error-theme';
1012
@import 'selection/pseudo-checkbox/pseudo-checkbox-theme';
1113
@import 'typography/all-typography';
1214

@@ -25,6 +27,7 @@
2527
@include mat-ripple();
2628
@include mat-option();
2729
@include mat-optgroup();
30+
@include mat-error();
2831
@include cdk-a11y();
2932
@include cdk-overlay();
3033
}
@@ -35,6 +38,7 @@
3538
@include mat-option-theme($theme);
3639
@include mat-optgroup-theme($theme);
3740
@include mat-pseudo-checkbox-theme($theme);
41+
@include mat-error-theme($theme);
3842

3943
// Wrapper element that provides the theme background when the
4044
// user's content isn't inside of a `md-sidenav-container`.

src/lib/core/core.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {OverlayModule} from './overlay/index';
1616
import {A11yModule} from './a11y/index';
1717
import {MdSelectionModule} from './selection/index';
1818
import {MdRippleModule} from './ripple/index';
19+
import {MdErrorModule} from './error/index';
1920

2021
// Re-exports of the CDK to avoid breaking changes.
2122
export {
@@ -123,12 +124,13 @@ export {
123124

124125
// Error
125126
export {
127+
MdErrorModule,
128+
MdError,
126129
ErrorStateMatcher,
127130
ErrorOptions,
128-
MD_ERROR_GLOBAL_OPTIONS,
129131
defaultErrorStateMatcher,
130-
showOnDirtyErrorStateMatcher
131-
} from './error/error-options';
132+
showOnDirtyErrorStateMatcher,
133+
} from './error/index';
132134

133135
@NgModule({
134136
imports: [
@@ -141,6 +143,7 @@ export {
141143
A11yModule,
142144
MdOptionModule,
143145
MdSelectionModule,
146+
MdErrorModule,
144147
],
145148
exports: [
146149
MdLineModule,
@@ -152,6 +155,7 @@ export {
152155
A11yModule,
153156
MdOptionModule,
154157
MdSelectionModule,
158+
MdErrorModule,
155159
],
156160
})
157161
export class MdCoreModule {}

src/lib/core/error/_error-theme.scss

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
@import '../theming/palette';
2+
@import '../theming/theming';
3+
4+
5+
@mixin mat-error-theme($theme) {
6+
.mat-error {
7+
color: mat-color(map-get($theme, warn));
8+
}
9+
}

src/lib/core/error/_error.scss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
@mixin mat-error {
2+
.mat-error {
3+
display: block;
4+
}
5+
}

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

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,12 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {InjectionToken} from '@angular/core';
9+
import {Injectable} from '@angular/core';
1010
import {FormGroupDirective, NgForm, NgControl} from '@angular/forms';
1111

12-
/** Injection token that can be used to specify the global error options. */
13-
export const MD_ERROR_GLOBAL_OPTIONS = new InjectionToken<ErrorOptions>('md-error-global-options');
14-
1512
export type ErrorStateMatcher =
1613
(control: NgControl | null, form: FormGroupDirective | NgForm | null) => boolean;
1714

18-
export interface ErrorOptions {
19-
errorStateMatcher?: ErrorStateMatcher;
20-
}
21-
2215
/** Returns whether control is invalid and is either touched or is a part of a submitted form. */
2316
export const defaultErrorStateMatcher: ErrorStateMatcher = (control, form) => {
2417
return control ? !!(control.invalid && (control.touched || (form && form.submitted))) : false;
@@ -28,3 +21,12 @@ export const defaultErrorStateMatcher: ErrorStateMatcher = (control, form) => {
2821
export const showOnDirtyErrorStateMatcher: ErrorStateMatcher = (control, form) => {
2922
return control ? !!(control.invalid && (control.dirty || (form && form.submitted))) : false;
3023
};
24+
25+
/**
26+
* Provider that defines how form controls behave with
27+
* regards to displaying error messages.
28+
*/
29+
@Injectable()
30+
export class ErrorOptions {
31+
errorStateMatcher: ErrorStateMatcher = defaultErrorStateMatcher;
32+
}

src/lib/core/error/error.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
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+
9+
import {Directive} from '@angular/core';
10+
11+
/** Single error message to be shown underneath a form control. */
12+
@Directive({
13+
selector: 'md-error, mat-error',
14+
host: {'class': 'mat-error'}
15+
})
16+
export class MdError { }

src/lib/core/error/index.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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+
9+
10+
import {NgModule} from '@angular/core';
11+
import {MdError} from './error';
12+
import {ErrorOptions} from './error-options';
13+
14+
@NgModule({
15+
declarations: [MdError],
16+
exports: [MdError],
17+
providers: [ErrorOptions],
18+
})
19+
export class MdErrorModule {}
20+
21+
22+
export * from './error';
23+
export * from './error-options';

src/lib/input/_input-theme.scss

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,6 @@
9494
background-color: $input-underline-color-warn;
9595
}
9696
}
97-
98-
.mat-input-error {
99-
color: $input-underline-color-warn;
100-
}
10197
}
10298

10399
// Applies a floating placeholder above the input itself.

src/lib/input/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88

99
import {NgModule} from '@angular/core';
1010
import {
11-
MdErrorDirective,
1211
MdHint,
1312
MdInputContainer,
1413
MdInputDirective,
@@ -19,11 +18,11 @@ import {
1918
import {MdTextareaAutosize} from './autosize';
2019
import {CommonModule} from '@angular/common';
2120
import {PlatformModule} from '../core/platform/index';
21+
import {MdErrorModule} from '../core/error/index';
2222

2323

2424
@NgModule({
2525
declarations: [
26-
MdErrorDirective,
2726
MdHint,
2827
MdInputContainer,
2928
MdInputDirective,
@@ -35,16 +34,17 @@ import {PlatformModule} from '../core/platform/index';
3534
imports: [
3635
CommonModule,
3736
PlatformModule,
37+
MdErrorModule,
3838
],
3939
exports: [
40-
MdErrorDirective,
4140
MdHint,
4241
MdInputContainer,
4342
MdInputDirective,
4443
MdPlaceholder,
4544
MdPrefix,
4645
MdSuffix,
4746
MdTextareaAutosize,
47+
MdErrorModule,
4848
],
4949
})
5050
export class MdInputModule {}

src/lib/input/input-container.scss

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -248,8 +248,3 @@ textarea.mat-input-element {
248248
.mat-input-hint-spacer {
249249
flex: 1 0 $mat-input-hint-min-space;
250250
}
251-
252-
// Single error message displayed beneath the input.
253-
.mat-input-error {
254-
display: block;
255-
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import {
2222
getMdInputContainerPlaceholderConflictError
2323
} from './input-container-errors';
2424
import {MD_PLACEHOLDER_GLOBAL_OPTIONS} from '../core/placeholder/placeholder-options';
25-
import {MD_ERROR_GLOBAL_OPTIONS, showOnDirtyErrorStateMatcher} from '../core/error/error-options';
25+
import {ErrorOptions, showOnDirtyErrorStateMatcher} from '../core/error/error-options';
2626

2727
describe('MdInputContainer without forms', function () {
2828
beforeEach(async(() => {
@@ -844,7 +844,7 @@ describe('MdInputContainer with forms', () => {
844844
],
845845
providers: [
846846
{
847-
provide: MD_ERROR_GLOBAL_OPTIONS,
847+
provide: ErrorOptions,
848848
useValue: { errorStateMatcher: globalErrorStateMatcher } }
849849
]
850850
});
@@ -875,7 +875,7 @@ describe('MdInputContainer with forms', () => {
875875
],
876876
providers: [
877877
{
878-
provide: MD_ERROR_GLOBAL_OPTIONS,
878+
provide: ErrorOptions,
879879
useValue: { errorStateMatcher: showOnDirtyErrorStateMatcher }
880880
}
881881
]

src/lib/input/input-container.ts

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,7 @@ import {
4444
MD_PLACEHOLDER_GLOBAL_OPTIONS,
4545
PlaceholderOptions
4646
} from '../core/placeholder/placeholder-options';
47-
import {
48-
defaultErrorStateMatcher,
49-
ErrorOptions,
50-
ErrorStateMatcher,
51-
MD_ERROR_GLOBAL_OPTIONS
52-
} from '../core/error/error-options';
47+
import {ErrorOptions, ErrorStateMatcher, MdError} from '../core/error/index';
5348
import {Subject} from 'rxjs/Subject';
5449

5550
// Invalid input type. Using one of these will throw an MdInputContainerUnsupportedTypeError.
@@ -96,14 +91,6 @@ export class MdHint {
9691
@Input() id: string = `md-input-hint-${nextUniqueId++}`;
9792
}
9893

99-
/** Single error message to be shown underneath the input. */
100-
@Directive({
101-
selector: 'md-error, mat-error',
102-
host: {
103-
'class': 'mat-input-error'
104-
}
105-
})
106-
export class MdErrorDirective { }
10794

10895
/** Prefix to be placed the the front of the input. */
10996
@Directive({
@@ -146,7 +133,6 @@ export class MdInputDirective implements OnChanges, OnDestroy, DoCheck {
146133
private _readonly = false;
147134
private _id: string;
148135
private _uid = `md-input-${nextUniqueId++}`;
149-
private _errorOptions: ErrorOptions;
150136
private _previousNativeValue = this.value;
151137

152138
/** Whether the input is in an error state. */
@@ -231,15 +217,13 @@ export class MdInputDirective implements OnChanges, OnDestroy, DoCheck {
231217
constructor(private _elementRef: ElementRef,
232218
private _renderer: Renderer2,
233219
private _platform: Platform,
220+
private _errorOptions: ErrorOptions,
234221
@Optional() @Self() public _ngControl: NgControl,
235222
@Optional() private _parentForm: NgForm,
236-
@Optional() private _parentFormGroup: FormGroupDirective,
237-
@Optional() @Inject(MD_ERROR_GLOBAL_OPTIONS) errorOptions: ErrorOptions) {
223+
@Optional() private _parentFormGroup: FormGroupDirective) {
238224

239225
// Force setter to be called in case id was not specified.
240226
this.id = this.id;
241-
this._errorOptions = errorOptions || {};
242-
this.errorStateMatcher = this._errorOptions.errorStateMatcher || defaultErrorStateMatcher;
243227

244228
// On some versions of iOS the caret gets stuck in the wrong place when holding down the delete
245229
// key. In order to get around this we need to "jiggle" the caret loose. Since this bug only
@@ -311,8 +295,8 @@ export class MdInputDirective implements OnChanges, OnDestroy, DoCheck {
311295
/** Re-evaluates the error state. This is only relevant with @angular/forms. */
312296
private _updateErrorState() {
313297
const oldState = this._isErrorState;
314-
const newState = this.errorStateMatcher(this._ngControl,
315-
this._parentFormGroup || this._parentForm);
298+
const errorMatcher = this.errorStateMatcher || this._errorOptions.errorStateMatcher;
299+
const newState = errorMatcher(this._ngControl, this._parentFormGroup || this._parentForm);
316300

317301
if (newState !== oldState) {
318302
this._isErrorState = newState;
@@ -450,7 +434,7 @@ export class MdInputContainer implements AfterViewInit, AfterContentInit, AfterC
450434
@ViewChild('underline') underlineRef: ElementRef;
451435
@ContentChild(MdInputDirective) _mdInputChild: MdInputDirective;
452436
@ContentChild(MdPlaceholder) _placeholderChild: MdPlaceholder;
453-
@ContentChildren(MdErrorDirective) _errorChildren: QueryList<MdErrorDirective>;
437+
@ContentChildren(MdError) _errorChildren: QueryList<MdError>;
454438
@ContentChildren(MdHint) _hintChildren: QueryList<MdHint>;
455439
@ContentChildren(MdPrefix) _prefixChildren: QueryList<MdPrefix>;
456440
@ContentChildren(MdSuffix) _suffixChildren: QueryList<MdSuffix>;

src/lib/input/input.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -132,14 +132,14 @@ function myErrorStateMatcher(control: FormControl, form: FormGroupDirective | Ng
132132
}
133133
```
134134

135-
A global error state matcher can be specified by setting the `MD_ERROR_GLOBAL_OPTIONS` provider. This applies
136-
to all inputs. For convenience, `showOnDirtyErrorStateMatcher` is available in order to globally cause
137-
input errors to show when the input is dirty and invalid.
135+
A global error state matcher can be specified by setting the `ErrorOptions` provider. This applies
136+
to all inputs. For convenience, `showOnDirtyErrorStateMatcher` is available in order to globally
137+
cause input errors to show when the input is dirty and invalid.
138138

139139
```ts
140140
@NgModule({
141141
providers: [
142-
{provide: MD_ERROR_GLOBAL_OPTIONS, useValue: { errorStateMatcher: showOnDirtyErrorStateMatcher }}
142+
{provide: ErrorOptions, useValue: { errorStateMatcher: showOnDirtyErrorStateMatcher }}
143143
]
144144
})
145145
```

src/lib/select/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
import {NgModule} from '@angular/core';
1010
import {CommonModule} from '@angular/common';
1111
import {MdSelect, MD_SELECT_SCROLL_STRATEGY_PROVIDER} from './select';
12-
import {MdCommonModule, OverlayModule, MdOptionModule} from '../core';
12+
import {MdCommonModule, OverlayModule, MdOptionModule, MdErrorModule} from '../core';
1313

1414

1515
@NgModule({
@@ -18,8 +18,9 @@ import {MdCommonModule, OverlayModule, MdOptionModule} from '../core';
1818
OverlayModule,
1919
MdOptionModule,
2020
MdCommonModule,
21+
MdErrorModule,
2122
],
22-
exports: [MdSelect, MdOptionModule, MdCommonModule],
23+
exports: [MdSelect, MdOptionModule, MdCommonModule, MdErrorModule],
2324
declarations: [MdSelect],
2425
providers: [MD_SELECT_SCROLL_STRATEGY_PROVIDER]
2526
})

src/lib/select/select.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import {Subject} from 'rxjs/Subject';
3131
import {ViewportRuler} from '../core/overlay/position/viewport-ruler';
3232
import {dispatchFakeEvent, dispatchKeyboardEvent, wrappedErrorMessage} from '@angular/cdk/testing';
3333
import {ScrollDispatcher} from '../core/overlay/scroll/scroll-dispatcher';
34-
import {MD_ERROR_GLOBAL_OPTIONS, ErrorOptions} from '../core/error/error-options';
34+
import {ErrorOptions} from '../core/error/error-options';
3535
import {
3636
FloatPlaceholderType,
3737
MD_PLACEHOLDER_GLOBAL_OPTIONS
@@ -2691,7 +2691,7 @@ describe('MdSelect', () => {
26912691
TestBed.resetTestingModule().configureTestingModule({
26922692
imports: [MdSelectModule, ReactiveFormsModule, FormsModule, NoopAnimationsModule],
26932693
declarations: [SelectInsideFormGroup],
2694-
providers: [{ provide: MD_ERROR_GLOBAL_OPTIONS, useValue: errorOptions }],
2694+
providers: [{ provide: ErrorOptions, useValue: errorOptions }],
26952695
});
26962696

26972697
const errorFixture = TestBed.createComponent(SelectInsideFormGroup);

0 commit comments

Comments
 (0)