Skip to content

Commit

Permalink
refactor: split error component into module
Browse files Browse the repository at this point in the history
  • Loading branch information
crisbeto committed Aug 2, 2017
1 parent 9c78bc2 commit 062ceb9
Show file tree
Hide file tree
Showing 16 changed files with 100 additions and 70 deletions.
4 changes: 4 additions & 0 deletions src/lib/core/_core.scss
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
@import 'option/option-theme';
@import 'option/optgroup';
@import 'option/optgroup-theme';
@import 'error/error';
@import 'error/error-theme';
@import 'selection/pseudo-checkbox/pseudo-checkbox-theme';
@import 'typography/all-typography';

Expand All @@ -25,6 +27,7 @@
@include mat-ripple();
@include mat-option();
@include mat-optgroup();
@include mat-error();
@include cdk-a11y();
@include cdk-overlay();
}
Expand All @@ -35,6 +38,7 @@
@include mat-option-theme($theme);
@include mat-optgroup-theme($theme);
@include mat-pseudo-checkbox-theme($theme);
@include mat-error-theme($theme);

// Wrapper element that provides the theme background when the
// user's content isn't inside of a `md-sidenav-container`.
Expand Down
10 changes: 7 additions & 3 deletions src/lib/core/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {OverlayModule} from './overlay/index';
import {A11yModule} from './a11y/index';
import {MdSelectionModule} from './selection/index';
import {MdRippleModule} from './ripple/index';
import {MdErrorModule} from './error/index';

// Re-exports of the CDK to avoid breaking changes.
export {
Expand Down Expand Up @@ -123,12 +124,13 @@ export {

// Error
export {
MdErrorModule,
MdError,
ErrorStateMatcher,
ErrorOptions,
MD_ERROR_GLOBAL_OPTIONS,
defaultErrorStateMatcher,
showOnDirtyErrorStateMatcher
} from './error/error-options';
showOnDirtyErrorStateMatcher,
} from './error/index';

@NgModule({
imports: [
Expand All @@ -141,6 +143,7 @@ export {
A11yModule,
MdOptionModule,
MdSelectionModule,
MdErrorModule,
],
exports: [
MdLineModule,
Expand All @@ -152,6 +155,7 @@ export {
A11yModule,
MdOptionModule,
MdSelectionModule,
MdErrorModule,
],
})
export class MdCoreModule {}
9 changes: 9 additions & 0 deletions src/lib/core/error/_error-theme.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@import '../theming/palette';
@import '../theming/theming';


@mixin mat-error-theme($theme) {
.mat-error {
color: mat-color(map-get($theme, warn));
}
}
5 changes: 5 additions & 0 deletions src/lib/core/error/_error.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@mixin mat-error {
.mat-error {
display: block;
}
}
18 changes: 10 additions & 8 deletions src/lib/core/error/error-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,12 @@
* found in the LICENSE file at https://angular.io/license
*/

import {InjectionToken} from '@angular/core';
import {Injectable} from '@angular/core';
import {FormGroupDirective, NgForm, NgControl} from '@angular/forms';

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

export type ErrorStateMatcher =
(control: NgControl | null, form: FormGroupDirective | NgForm | null) => boolean;

export interface ErrorOptions {
errorStateMatcher?: ErrorStateMatcher;
}

/** Returns whether control is invalid and is either touched or is a part of a submitted form. */
export const defaultErrorStateMatcher: ErrorStateMatcher = (control, form) => {
return control ? !!(control.invalid && (control.touched || (form && form.submitted))) : false;
Expand All @@ -28,3 +21,12 @@ export const defaultErrorStateMatcher: ErrorStateMatcher = (control, form) => {
export const showOnDirtyErrorStateMatcher: ErrorStateMatcher = (control, form) => {
return control ? !!(control.invalid && (control.dirty || (form && form.submitted))) : false;
};

/**
* Provider that defines how form controls behave with
* regards to displaying error messages.
*/
@Injectable()
export class ErrorOptions {
errorStateMatcher: ErrorStateMatcher = defaultErrorStateMatcher;
}
16 changes: 16 additions & 0 deletions src/lib/core/error/error.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

import {Directive} from '@angular/core';

/** Single error message to be shown underneath a form control. */
@Directive({
selector: 'md-error, mat-error',
host: {'class': 'mat-error'}
})
export class MdError { }
23 changes: 23 additions & 0 deletions src/lib/core/error/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/


import {NgModule} from '@angular/core';
import {MdError} from './error';
import {ErrorOptions} from './error-options';

@NgModule({
declarations: [MdError],
exports: [MdError],
providers: [ErrorOptions],
})
export class MdErrorModule {}


export * from './error';
export * from './error-options';
4 changes: 0 additions & 4 deletions src/lib/input/_input-theme.scss
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,6 @@
background-color: $input-underline-color-warn;
}
}

.mat-input-error {
color: $input-underline-color-warn;
}
}

// Applies a floating placeholder above the input itself.
Expand Down
6 changes: 3 additions & 3 deletions src/lib/input/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

import {NgModule} from '@angular/core';
import {
MdErrorDirective,
MdHint,
MdInputContainer,
MdInputDirective,
Expand All @@ -19,11 +18,11 @@ import {
import {MdTextareaAutosize} from './autosize';
import {CommonModule} from '@angular/common';
import {PlatformModule} from '../core/platform/index';
import {MdErrorModule} from '../core/error/index';


@NgModule({
declarations: [
MdErrorDirective,
MdHint,
MdInputContainer,
MdInputDirective,
Expand All @@ -35,16 +34,17 @@ import {PlatformModule} from '../core/platform/index';
imports: [
CommonModule,
PlatformModule,
MdErrorModule,
],
exports: [
MdErrorDirective,
MdHint,
MdInputContainer,
MdInputDirective,
MdPlaceholder,
MdPrefix,
MdSuffix,
MdTextareaAutosize,
MdErrorModule,
],
})
export class MdInputModule {}
Expand Down
5 changes: 0 additions & 5 deletions src/lib/input/input-container.scss
Original file line number Diff line number Diff line change
Expand Up @@ -248,8 +248,3 @@ textarea.mat-input-element {
.mat-input-hint-spacer {
flex: 1 0 $mat-input-hint-min-space;
}

// Single error message displayed beneath the input.
.mat-input-error {
display: block;
}
6 changes: 3 additions & 3 deletions src/lib/input/input-container.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
getMdInputContainerPlaceholderConflictError
} from './input-container-errors';
import {MD_PLACEHOLDER_GLOBAL_OPTIONS} from '../core/placeholder/placeholder-options';
import {MD_ERROR_GLOBAL_OPTIONS, showOnDirtyErrorStateMatcher} from '../core/error/error-options';
import {ErrorOptions, showOnDirtyErrorStateMatcher} from '../core/error/error-options';

describe('MdInputContainer without forms', function () {
beforeEach(async(() => {
Expand Down Expand Up @@ -844,7 +844,7 @@ describe('MdInputContainer with forms', () => {
],
providers: [
{
provide: MD_ERROR_GLOBAL_OPTIONS,
provide: ErrorOptions,
useValue: { errorStateMatcher: globalErrorStateMatcher } }
]
});
Expand Down Expand Up @@ -875,7 +875,7 @@ describe('MdInputContainer with forms', () => {
],
providers: [
{
provide: MD_ERROR_GLOBAL_OPTIONS,
provide: ErrorOptions,
useValue: { errorStateMatcher: showOnDirtyErrorStateMatcher }
}
]
Expand Down
28 changes: 6 additions & 22 deletions src/lib/input/input-container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,7 @@ import {
MD_PLACEHOLDER_GLOBAL_OPTIONS,
PlaceholderOptions
} from '../core/placeholder/placeholder-options';
import {
defaultErrorStateMatcher,
ErrorOptions,
ErrorStateMatcher,
MD_ERROR_GLOBAL_OPTIONS
} from '../core/error/error-options';
import {ErrorOptions, ErrorStateMatcher, MdError} from '../core/error/index';
import {Subject} from 'rxjs/Subject';

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

/** Single error message to be shown underneath the input. */
@Directive({
selector: 'md-error, mat-error',
host: {
'class': 'mat-input-error'
}
})
export class MdErrorDirective { }

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

/** Whether the input is in an error state. */
Expand Down Expand Up @@ -231,15 +217,13 @@ export class MdInputDirective implements OnChanges, OnDestroy, DoCheck {
constructor(private _elementRef: ElementRef,
private _renderer: Renderer2,
private _platform: Platform,
private _errorOptions: ErrorOptions,
@Optional() @Self() public _ngControl: NgControl,
@Optional() private _parentForm: NgForm,
@Optional() private _parentFormGroup: FormGroupDirective,
@Optional() @Inject(MD_ERROR_GLOBAL_OPTIONS) errorOptions: ErrorOptions) {
@Optional() private _parentFormGroup: FormGroupDirective) {

// Force setter to be called in case id was not specified.
this.id = this.id;
this._errorOptions = errorOptions || {};
this.errorStateMatcher = this._errorOptions.errorStateMatcher || defaultErrorStateMatcher;

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

if (newState !== oldState) {
this._isErrorState = newState;
Expand Down Expand Up @@ -450,7 +434,7 @@ export class MdInputContainer implements AfterViewInit, AfterContentInit, AfterC
@ViewChild('underline') underlineRef: ElementRef;
@ContentChild(MdInputDirective) _mdInputChild: MdInputDirective;
@ContentChild(MdPlaceholder) _placeholderChild: MdPlaceholder;
@ContentChildren(MdErrorDirective) _errorChildren: QueryList<MdErrorDirective>;
@ContentChildren(MdError) _errorChildren: QueryList<MdError>;
@ContentChildren(MdHint) _hintChildren: QueryList<MdHint>;
@ContentChildren(MdPrefix) _prefixChildren: QueryList<MdPrefix>;
@ContentChildren(MdSuffix) _suffixChildren: QueryList<MdSuffix>;
Expand Down
8 changes: 4 additions & 4 deletions src/lib/input/input.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,14 +132,14 @@ function myErrorStateMatcher(control: FormControl, form: FormGroupDirective | Ng
}
```

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

```ts
@NgModule({
providers: [
{provide: MD_ERROR_GLOBAL_OPTIONS, useValue: { errorStateMatcher: showOnDirtyErrorStateMatcher }}
{provide: ErrorOptions, useValue: { errorStateMatcher: showOnDirtyErrorStateMatcher }}
]
})
```
Expand Down
5 changes: 3 additions & 2 deletions src/lib/select/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {MdSelect, MD_SELECT_SCROLL_STRATEGY_PROVIDER} from './select';
import {MdCommonModule, OverlayModule, MdOptionModule} from '../core';
import {MdCommonModule, OverlayModule, MdOptionModule, MdErrorModule} from '../core';


@NgModule({
Expand All @@ -18,8 +18,9 @@ import {MdCommonModule, OverlayModule, MdOptionModule} from '../core';
OverlayModule,
MdOptionModule,
MdCommonModule,
MdErrorModule,
],
exports: [MdSelect, MdOptionModule, MdCommonModule],
exports: [MdSelect, MdOptionModule, MdCommonModule, MdErrorModule],
declarations: [MdSelect],
providers: [MD_SELECT_SCROLL_STRATEGY_PROVIDER]
})
Expand Down
4 changes: 2 additions & 2 deletions src/lib/select/select.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import {Subject} from 'rxjs/Subject';
import {ViewportRuler} from '../core/overlay/position/viewport-ruler';
import {dispatchFakeEvent, dispatchKeyboardEvent, wrappedErrorMessage} from '@angular/cdk/testing';
import {ScrollDispatcher} from '../core/overlay/scroll/scroll-dispatcher';
import {MD_ERROR_GLOBAL_OPTIONS, ErrorOptions} from '../core/error/error-options';
import {ErrorOptions} from '../core/error/error-options';
import {
FloatPlaceholderType,
MD_PLACEHOLDER_GLOBAL_OPTIONS
Expand Down Expand Up @@ -2691,7 +2691,7 @@ describe('MdSelect', () => {
TestBed.resetTestingModule().configureTestingModule({
imports: [MdSelectModule, ReactiveFormsModule, FormsModule, NoopAnimationsModule],
declarations: [SelectInsideFormGroup],
providers: [{ provide: MD_ERROR_GLOBAL_OPTIONS, useValue: errorOptions }],
providers: [{ provide: ErrorOptions, useValue: errorOptions }],
});

const errorFixture = TestBed.createComponent(SelectInsideFormGroup);
Expand Down
Loading

0 comments on commit 062ceb9

Please sign in to comment.