@@ -23,6 +23,7 @@ import {
2323 getMdInputContainerPlaceholderConflictError
2424} from './input-container-errors' ;
2525import { MD_PLACEHOLDER_GLOBAL_OPTIONS } from '../core/placeholder/placeholder-options' ;
26+ import { MD_ERROR_GLOBAL_OPTIONS , showOnDirtyErrorStateMatcher } from '../core/error/error-options' ;
2627
2728describe ( 'MdInputContainer' , function ( ) {
2829 beforeEach ( async ( ( ) => {
@@ -56,6 +57,7 @@ describe('MdInputContainer', function () {
5657 MdInputContainerWithDynamicPlaceholder ,
5758 MdInputContainerWithFormControl ,
5859 MdInputContainerWithFormErrorMessages ,
60+ MdInputContainerWithCustomErrorStateMatcher ,
5961 MdInputContainerWithFormGroupErrorMessages ,
6062 MdInputContainerWithId ,
6163 MdInputContainerWithPrefixAndSuffix ,
@@ -749,6 +751,113 @@ describe('MdInputContainer', function () {
749751
750752 } ) ;
751753
754+ describe ( 'custom error behavior' , ( ) => {
755+ it ( 'should display an error message when a custom error matcher returns true' , ( ) => {
756+ let fixture = TestBed . createComponent ( MdInputContainerWithCustomErrorStateMatcher ) ;
757+ fixture . detectChanges ( ) ;
758+
759+ let component = fixture . componentInstance ;
760+ let containerEl = fixture . debugElement . query ( By . css ( 'md-input-container' ) ) . nativeElement ;
761+
762+ const control = component . formGroup . get ( 'name' ) ! ;
763+
764+ expect ( control . invalid ) . toBe ( true , 'Expected form control to be invalid' ) ;
765+ expect ( containerEl . querySelectorAll ( 'md-error' ) . length )
766+ . toBe ( 0 , 'Expected no error messages' ) ;
767+
768+ control . markAsTouched ( ) ;
769+ fixture . detectChanges ( ) ;
770+
771+ expect ( containerEl . querySelectorAll ( 'md-error' ) . length )
772+ . toBe ( 0 , 'Expected no error messages after being touched.' ) ;
773+
774+ component . errorState = true ;
775+ fixture . detectChanges ( ) ;
776+
777+ expect ( containerEl . querySelectorAll ( 'md-error' ) . length )
778+ . toBe ( 1 , 'Expected one error messages to have been rendered.' ) ;
779+ } ) ;
780+
781+ it ( 'should display an error message when global error matcher returns true' , ( ) => {
782+
783+ // Global error state matcher that will always cause errors to show
784+ function globalErrorStateMatcher ( ) {
785+ return true ;
786+ }
787+
788+ TestBed . resetTestingModule ( ) ;
789+ TestBed . configureTestingModule ( {
790+ imports : [
791+ FormsModule ,
792+ MdInputModule ,
793+ NoopAnimationsModule ,
794+ ReactiveFormsModule ,
795+ ] ,
796+ declarations : [
797+ MdInputContainerWithFormErrorMessages
798+ ] ,
799+ providers : [
800+ {
801+ provide : MD_ERROR_GLOBAL_OPTIONS ,
802+ useValue : { errorStateMatcher : globalErrorStateMatcher } }
803+ ]
804+ } ) ;
805+
806+ let fixture = TestBed . createComponent ( MdInputContainerWithFormErrorMessages ) ;
807+
808+ fixture . detectChanges ( ) ;
809+
810+ let containerEl = fixture . debugElement . query ( By . css ( 'md-input-container' ) ) . nativeElement ;
811+ let testComponent = fixture . componentInstance ;
812+
813+ // Expect the control to still be untouched but the error to show due to the global setting
814+ expect ( testComponent . formControl . untouched ) . toBe ( true , 'Expected untouched form control' ) ;
815+ expect ( containerEl . querySelectorAll ( 'md-error' ) . length ) . toBe ( 1 , 'Expected an error message' ) ;
816+ } ) ;
817+
818+ it ( 'should display an error message when using showOnDirtyErrorStateMatcher' , async ( ( ) => {
819+ TestBed . resetTestingModule ( ) ;
820+ TestBed . configureTestingModule ( {
821+ imports : [
822+ FormsModule ,
823+ MdInputModule ,
824+ NoopAnimationsModule ,
825+ ReactiveFormsModule ,
826+ ] ,
827+ declarations : [
828+ MdInputContainerWithFormErrorMessages
829+ ] ,
830+ providers : [
831+ {
832+ provide : MD_ERROR_GLOBAL_OPTIONS ,
833+ useValue : { errorStateMatcher : showOnDirtyErrorStateMatcher }
834+ }
835+ ]
836+ } ) ;
837+
838+ let fixture = TestBed . createComponent ( MdInputContainerWithFormErrorMessages ) ;
839+ fixture . detectChanges ( ) ;
840+
841+ let containerEl = fixture . debugElement . query ( By . css ( 'md-input-container' ) ) . nativeElement ;
842+ let testComponent = fixture . componentInstance ;
843+
844+ expect ( testComponent . formControl . invalid ) . toBe ( true , 'Expected form control to be invalid' ) ;
845+ expect ( containerEl . querySelectorAll ( 'md-error' ) . length ) . toBe ( 0 , 'Expected no error messages' ) ;
846+
847+ testComponent . formControl . markAsTouched ( ) ;
848+ fixture . detectChanges ( ) ;
849+
850+ expect ( containerEl . querySelectorAll ( 'md-error' ) . length )
851+ . toBe ( 0 , 'Expected no error messages when touched' ) ;
852+
853+ testComponent . formControl . markAsDirty ( ) ;
854+ fixture . detectChanges ( ) ;
855+
856+ expect ( containerEl . querySelectorAll ( 'md-error' ) . length )
857+ . toBe ( 1 , 'Expected one error message when dirty' ) ;
858+ } ) ) ;
859+ } ) ;
860+
752861 it ( 'should not have prefix and suffix elements when none are specified' , ( ) => {
753862 let fixture = TestBed . createComponent ( MdInputContainerWithId ) ;
754863 fixture . detectChanges ( ) ;
@@ -1018,6 +1127,31 @@ class MdInputContainerWithFormErrorMessages {
10181127 renderError = true ;
10191128}
10201129
1130+ @Component ( {
1131+ template : `
1132+ <form [formGroup]="formGroup">
1133+ <md-input-container>
1134+ <input mdInput
1135+ formControlName="name"
1136+ [errorStateMatcher]="customErrorStateMatcher.bind(this)">
1137+ <md-hint>Please type something</md-hint>
1138+ <md-error>This field is required</md-error>
1139+ </md-input-container>
1140+ </form>
1141+ `
1142+ } )
1143+ class MdInputContainerWithCustomErrorStateMatcher {
1144+ formGroup = new FormGroup ( {
1145+ name : new FormControl ( '' , Validators . required )
1146+ } ) ;
1147+
1148+ errorState = false ;
1149+
1150+ customErrorStateMatcher ( ) : boolean {
1151+ return this . errorState ;
1152+ }
1153+ }
1154+
10211155@Component ( {
10221156 template : `
10231157 <form [formGroup]="formGroup" novalidate>
0 commit comments