@@ -24,6 +24,8 @@ import {
24
24
} from '@angular/core' ;
25
25
import { LEFT_ARROW , RIGHT_ARROW , ENTER , SPACE } from '@angular/cdk/keyboard' ;
26
26
import { CdkStepLabel } from './step-label' ;
27
+ import { coerceBooleanProperty } from '@angular/cdk/coercion' ;
28
+ import { AbstractControl } from '@angular/forms' ;
27
29
28
30
/** Used to generate unique ID for each stepper component. */
29
31
let nextId = 0 ;
@@ -45,7 +47,7 @@ export class CdkStepperSelectionEvent {
45
47
46
48
@Component ( {
47
49
selector : 'cdk-step' ,
48
- templateUrl : 'step.html' ,
50
+ templateUrl : 'step.html'
49
51
} )
50
52
export class CdkStep {
51
53
/** Template for step label if it exists. */
@@ -54,6 +56,17 @@ export class CdkStep {
54
56
/** Template for step content. */
55
57
@ViewChild ( TemplateRef ) content : TemplateRef < any > ;
56
58
59
+ /** The top level abstract control of the step. */
60
+ @Input ( )
61
+ get stepControl ( ) { return this . _stepControl ; }
62
+ set stepControl ( control : AbstractControl ) {
63
+ this . _stepControl = control ;
64
+ }
65
+ private _stepControl : AbstractControl ;
66
+
67
+ /** Whether user has seen the expanded step content or not . */
68
+ interacted = false ;
69
+
57
70
/** Label of the step. */
58
71
@Input ( )
59
72
label : string ;
@@ -70,7 +83,7 @@ export class CdkStep {
70
83
selector : 'cdk-stepper' ,
71
84
host : {
72
85
'(focus)' : '_focusStep()' ,
73
- '(keydown)' : '_onKeydown($event)' ,
86
+ '(keydown)' : '_onKeydown($event)'
74
87
} ,
75
88
} )
76
89
export class CdkStepper {
@@ -80,11 +93,17 @@ export class CdkStepper {
80
93
/** The list of step headers of the steps in the stepper. */
81
94
_stepHeader : QueryList < ElementRef > ;
82
95
96
+ /** Whether the validity of previous steps should be checked or not. */
97
+ @Input ( )
98
+ get linear ( ) { return this . _linear ; }
99
+ set linear ( value : any ) { this . _linear = coerceBooleanProperty ( value ) ; }
100
+ private _linear = false ;
101
+
83
102
/** The index of the selected step. */
84
103
@Input ( )
85
104
get selectedIndex ( ) { return this . _selectedIndex ; }
86
105
set selectedIndex ( index : number ) {
87
- if ( this . _selectedIndex != index ) {
106
+ if ( this . _selectedIndex != index && ! this . _anyControlsInvalid ( index ) ) {
88
107
this . _emitStepperSelectionEvent ( index ) ;
89
108
this . _focusStep ( this . _selectedIndex ) ;
90
109
}
@@ -153,7 +172,7 @@ export class CdkStepper {
153
172
break ;
154
173
case SPACE :
155
174
case ENTER :
156
- this . _emitStepperSelectionEvent ( this . _focusIndex ) ;
175
+ this . selectedIndex = this . _focusIndex ;
157
176
break ;
158
177
default :
159
178
// Return to avoid calling preventDefault on keys that are not explicitly handled.
@@ -166,4 +185,17 @@ export class CdkStepper {
166
185
this . _focusIndex = index ;
167
186
this . _stepHeader . toArray ( ) [ this . _focusIndex ] . nativeElement . focus ( ) ;
168
187
}
188
+
189
+ private _anyControlsInvalid ( index : number ) : boolean {
190
+ const stepsArray = this . _steps . toArray ( ) ;
191
+ stepsArray [ this . _selectedIndex ] . interacted = true ;
192
+ if ( this . _linear ) {
193
+ for ( let i = 0 ; i < index ; i ++ ) {
194
+ if ( ! stepsArray [ i ] . stepControl . valid ) {
195
+ return true ;
196
+ }
197
+ }
198
+ }
199
+ return false ;
200
+ }
169
201
}
0 commit comments