Skip to content

Commit fe6e3d2

Browse files
committed
refactor: split error component into module
1 parent 86de634 commit fe6e3d2

16 files changed

+108
-75
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: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
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, Input} from '@angular/core';
10+
11+
let nextUniqueId = 0;
12+
13+
/** Single error message to be shown underneath a form control. */
14+
@Directive({
15+
selector: 'md-error, mat-error',
16+
host: {
17+
'class': 'mat-error',
18+
'role': 'alert',
19+
'[attr.id]': 'id',
20+
}
21+
})
22+
export class MdError {
23+
@Input() id: string = `md-input-error-${nextUniqueId++}`;
24+
}

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
@@ -252,8 +252,3 @@ textarea.mat-input-element {
252252
.mat-input-hint-spacer {
253253
flex: 1 0 $mat-input-hint-min-space;
254254
}
255-
256-
// Single error message displayed beneath the input.
257-
.mat-input-error {
258-
display: block;
259-
}

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(() => {
@@ -897,7 +897,7 @@ describe('MdInputContainer with forms', () => {
897897
],
898898
providers: [
899899
{
900-
provide: MD_ERROR_GLOBAL_OPTIONS,
900+
provide: ErrorOptions,
901901
useValue: { errorStateMatcher: globalErrorStateMatcher } }
902902
]
903903
});
@@ -928,7 +928,7 @@ describe('MdInputContainer with forms', () => {
928928
],
929929
providers: [
930930
{
931-
provide: MD_ERROR_GLOBAL_OPTIONS,
931+
provide: ErrorOptions,
932932
useValue: { errorStateMatcher: showOnDirtyErrorStateMatcher }
933933
}
934934
]

src/lib/input/input-container.ts

Lines changed: 6 additions & 27 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
import {startWith} from '@angular/cdk/rxjs';
5550

@@ -97,19 +92,6 @@ export class MdHint {
9792
@Input() id: string = `md-input-hint-${nextUniqueId++}`;
9893
}
9994

100-
/** Single error message to be shown underneath the input. */
101-
@Directive({
102-
selector: 'md-error, mat-error',
103-
host: {
104-
'class': 'mat-input-error',
105-
'role': 'alert',
106-
'[attr.id]': 'id',
107-
}
108-
})
109-
export class MdErrorDirective {
110-
@Input() id: string = `md-input-error-${nextUniqueId++}`;
111-
}
112-
11395
/** Prefix to be placed the the front of the input. */
11496
@Directive({
11597
selector: '[mdPrefix], [matPrefix]'
@@ -151,7 +133,6 @@ export class MdInputDirective implements OnChanges, OnDestroy, DoCheck {
151133
private _readonly = false;
152134
private _id: string;
153135
private _uid = `md-input-${nextUniqueId++}`;
154-
private _errorOptions: ErrorOptions;
155136
private _previousNativeValue = this.value;
156137

157138
/** Whether the input is in an error state. */
@@ -241,15 +222,13 @@ export class MdInputDirective implements OnChanges, OnDestroy, DoCheck {
241222
constructor(private _elementRef: ElementRef,
242223
private _renderer: Renderer2,
243224
private _platform: Platform,
225+
private _errorOptions: ErrorOptions,
244226
@Optional() @Self() public _ngControl: NgControl,
245227
@Optional() private _parentForm: NgForm,
246-
@Optional() private _parentFormGroup: FormGroupDirective,
247-
@Optional() @Inject(MD_ERROR_GLOBAL_OPTIONS) errorOptions: ErrorOptions) {
228+
@Optional() private _parentFormGroup: FormGroupDirective) {
248229

249230
// Force setter to be called in case id was not specified.
250231
this.id = this.id;
251-
this._errorOptions = errorOptions || {};
252-
this.errorStateMatcher = this._errorOptions.errorStateMatcher || defaultErrorStateMatcher;
253232

254233
// On some versions of iOS the caret gets stuck in the wrong place when holding down the delete
255234
// key. In order to get around this we need to "jiggle" the caret loose. Since this bug only
@@ -321,8 +300,8 @@ export class MdInputDirective implements OnChanges, OnDestroy, DoCheck {
321300
/** Re-evaluates the error state. This is only relevant with @angular/forms. */
322301
private _updateErrorState() {
323302
const oldState = this._isErrorState;
324-
const newState = this.errorStateMatcher(this._ngControl,
325-
this._parentFormGroup || this._parentForm);
303+
const errorMatcher = this.errorStateMatcher || this._errorOptions.errorStateMatcher;
304+
const newState = errorMatcher(this._ngControl, this._parentFormGroup || this._parentForm);
326305

327306
if (newState !== oldState) {
328307
this._isErrorState = newState;
@@ -463,7 +442,7 @@ export class MdInputContainer implements AfterViewInit, AfterContentInit, AfterC
463442
@ViewChild('underline') underlineRef: ElementRef;
464443
@ContentChild(MdInputDirective) _mdInputChild: MdInputDirective;
465444
@ContentChild(MdPlaceholder) _placeholderChild: MdPlaceholder;
466-
@ContentChildren(MdErrorDirective) _errorChildren: QueryList<MdErrorDirective>;
445+
@ContentChildren(MdError) _errorChildren: QueryList<MdError>;
467446
@ContentChildren(MdHint) _hintChildren: QueryList<MdHint>;
468447
@ContentChildren(MdPrefix) _prefixChildren: QueryList<MdPrefix>;
469448
@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
@@ -2706,7 +2706,7 @@ describe('MdSelect', () => {
27062706
TestBed.resetTestingModule().configureTestingModule({
27072707
imports: [MdSelectModule, ReactiveFormsModule, FormsModule, NoopAnimationsModule],
27082708
declarations: [SelectInsideFormGroup],
2709-
providers: [{ provide: MD_ERROR_GLOBAL_OPTIONS, useValue: errorOptions }],
2709+
providers: [{ provide: ErrorOptions, useValue: errorOptions }],
27102710
});
27112711

27122712
const errorFixture = TestBed.createComponent(SelectInsideFormGroup);

0 commit comments

Comments
 (0)