@@ -37,6 +37,43 @@ export interface HasErrorState {
37
37
stateChanges : Subject < void > ;
38
38
}
39
39
40
+ /**
41
+ * Class that tracks the error state of a component.
42
+ * @docs -private
43
+ */
44
+ export class _ErrorStateTracker {
45
+ /** Whether the tracker is currently in an error state. */
46
+ errorState = false ;
47
+
48
+ /** User-defined matcher for the error state. */
49
+ matcher : ErrorStateMatcher ;
50
+
51
+ constructor (
52
+ private _defaultMatcher : ErrorStateMatcher | null ,
53
+ public ngControl : NgControl | null ,
54
+ private _parentFormGroup : FormGroupDirective | null ,
55
+ private _parentForm : NgForm | null ,
56
+ private _stateChanges : Subject < void > ,
57
+ ) { }
58
+
59
+ /** Updates the error state based on the provided error state matcher. */
60
+ updateErrorState ( ) {
61
+ const oldState = this . errorState ;
62
+ const parent = this . _parentFormGroup || this . _parentForm ;
63
+ const matcher = this . matcher || this . _defaultMatcher ;
64
+ const control = this . ngControl ? ( this . ngControl . control as AbstractControl ) : null ;
65
+ // Note: the null check here shouldn't be necessary, but there's an internal
66
+ // test that appears to pass an object whose `isErrorState` isn't a function.
67
+ const newState =
68
+ typeof matcher ?. isErrorState === 'function' ? matcher . isErrorState ( control , parent ) : false ;
69
+
70
+ if ( newState !== oldState ) {
71
+ this . errorState = newState ;
72
+ this . _stateChanges . next ( ) ;
73
+ }
74
+ }
75
+ }
76
+
40
77
/**
41
78
* Mixin to augment a directive with updateErrorState method.
42
79
* For component with `errorState` and need to update `errorState`.
@@ -48,24 +85,41 @@ export function mixinErrorState<T extends Constructor<HasErrorState>>(
48
85
base : T ,
49
86
) : CanUpdateErrorStateCtor & T {
50
87
return class extends base {
88
+ private _tracker : _ErrorStateTracker | undefined ;
89
+
51
90
/** Whether the component is in an error state. */
52
- errorState : boolean = false ;
91
+ get errorState ( ) {
92
+ return this . _getTracker ( ) . errorState ;
93
+ }
94
+ set errorState ( value : boolean ) {
95
+ this . _getTracker ( ) . errorState = value ;
96
+ }
53
97
54
98
/** An object used to control the error state of the component. */
55
- errorStateMatcher : ErrorStateMatcher ;
99
+ get errorStateMatcher ( ) {
100
+ return this . _getTracker ( ) . matcher ;
101
+ }
102
+ set errorStateMatcher ( value : ErrorStateMatcher ) {
103
+ this . _getTracker ( ) . matcher = value ;
104
+ }
56
105
57
106
/** Updates the error state based on the provided error state matcher. */
58
107
updateErrorState ( ) {
59
- const oldState = this . errorState ;
60
- const parent = this . _parentFormGroup || this . _parentForm ;
61
- const matcher = this . errorStateMatcher || this . _defaultErrorStateMatcher ;
62
- const control = this . ngControl ? ( this . ngControl . control as AbstractControl ) : null ;
63
- const newState = matcher . isErrorState ( control , parent ) ;
64
-
65
- if ( newState !== oldState ) {
66
- this . errorState = newState ;
67
- this . stateChanges . next ( ) ;
108
+ this . _getTracker ( ) . updateErrorState ( ) ;
109
+ }
110
+
111
+ private _getTracker ( ) {
112
+ if ( ! this . _tracker ) {
113
+ this . _tracker = new _ErrorStateTracker (
114
+ this . _defaultErrorStateMatcher ,
115
+ this . ngControl ,
116
+ this . _parentFormGroup ,
117
+ this . _parentForm ,
118
+ this . stateChanges ,
119
+ ) ;
68
120
}
121
+
122
+ return this . _tracker ;
69
123
}
70
124
71
125
constructor ( ...args : any [ ] ) {
0 commit comments