@@ -24,6 +24,8 @@ import {
2424} from '@angular/core' ;
2525import { LEFT_ARROW , RIGHT_ARROW , ENTER , SPACE } from '@angular/cdk/keyboard' ;
2626import { CdkStepLabel } from './step-label' ;
27+ import { coerceBooleanProperty } from '@angular/cdk/coercion' ;
28+ import { AbstractControl } from '@angular/forms' ;
2729
2830/** Used to generate unique ID for each stepper component. */
2931let nextId = 0 ;
@@ -45,7 +47,7 @@ export class CdkStepperSelectionEvent {
4547
4648@Component ( {
4749 selector : 'cdk-step' ,
48- templateUrl : 'step.html' ,
50+ templateUrl : 'step.html'
4951} )
5052export class CdkStep {
5153 /** Template for step label if it exists. */
@@ -54,6 +56,17 @@ export class CdkStep {
5456 /** Template for step content. */
5557 @ViewChild ( TemplateRef ) content : TemplateRef < any > ;
5658
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+
5770 /** Label of the step. */
5871 @Input ( )
5972 label : string ;
@@ -70,7 +83,7 @@ export class CdkStep {
7083 selector : 'cdk-stepper' ,
7184 host : {
7285 '(focus)' : '_focusStep()' ,
73- '(keydown)' : '_onKeydown($event)' ,
86+ '(keydown)' : '_onKeydown($event)'
7487 } ,
7588} )
7689export class CdkStepper {
@@ -80,11 +93,17 @@ export class CdkStepper {
8093 /** The list of step headers of the steps in the stepper. */
8194 _stepHeader : QueryList < ElementRef > ;
8295
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+
83102 /** The index of the selected step. */
84103 @Input ( )
85104 get selectedIndex ( ) { return this . _selectedIndex ; }
86105 set selectedIndex ( index : number ) {
87- if ( this . _selectedIndex != index ) {
106+ if ( this . _selectedIndex != index && ! this . _anyControlsInvalid ( index ) ) {
88107 this . _emitStepperSelectionEvent ( index ) ;
89108 this . _focusStep ( this . _selectedIndex ) ;
90109 }
@@ -153,7 +172,7 @@ export class CdkStepper {
153172 break ;
154173 case SPACE :
155174 case ENTER :
156- this . _emitStepperSelectionEvent ( this . _focusIndex ) ;
175+ this . selectedIndex = this . _focusIndex ;
157176 break ;
158177 default :
159178 // Return to avoid calling preventDefault on keys that are not explicitly handled.
@@ -166,4 +185,17 @@ export class CdkStepper {
166185 this . _focusIndex = index ;
167186 this . _stepHeader . toArray ( ) [ this . _focusIndex ] . nativeElement . focus ( ) ;
168187 }
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+ }
169201}
0 commit comments