@@ -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