@@ -40,6 +40,7 @@ import {
4040 type NgForm ,
4141 type FormGroupDirective ,
4242} from '@angular/forms' ;
43+ import type { Field } from '@angular/forms/signals' ;
4344import { _getFocusedElementPierceShadowDom } from '../platform' ;
4445import { Observable , of as observableOf , Subject } from 'rxjs' ;
4546import { startWith , takeUntil } from 'rxjs/operators' ;
@@ -56,6 +57,9 @@ export type StepContentPositionState = 'previous' | 'current' | 'next';
5657/** Possible orientation of a stepper. */
5758export type StepperOrientation = 'horizontal' | 'vertical' ;
5859
60+ /** Possible controls that can be assigned to a step. */
61+ export type StepControl = AbstractControl | Field < unknown > ;
62+
5963/** Change event emitted on selection changes. */
6064export class StepperSelectionEvent {
6165 /** Index of the step now selected. */
@@ -132,7 +136,7 @@ export class CdkStep implements OnChanges {
132136 @ViewChild ( TemplateRef , { static : true } ) content ! : TemplateRef < any > ;
133137
134138 /** The top level abstract control of the step. */
135- @Input ( ) stepControl ! : AbstractControl ;
139+ @Input ( ) stepControl ! : StepControl ;
136140
137141 /** Whether user has attempted to move away from the step. */
138142 get interacted ( ) : boolean {
@@ -195,7 +199,7 @@ export class CdkStep implements OnChanges {
195199 return override ;
196200 }
197201
198- return interacted && ( ! this . stepControl || this . stepControl . valid ) ;
202+ return interacted && ( ! this . stepControl || isValid ( this . stepControl ) ) ;
199203 }
200204 set completed ( value : boolean ) {
201205 this . _completedOverride . set ( value ) ;
@@ -253,7 +257,7 @@ export class CdkStep implements OnChanges {
253257 private _customError = signal < boolean | null > ( null ) ;
254258
255259 private _getDefaultError ( ) {
256- return this . interacted && ! ! this . stepControl ?. invalid ;
260+ return this . interacted && ! ! this . stepControl && isInvalid ( this . stepControl ) ;
257261 }
258262
259263 constructor ( ) {
@@ -284,7 +288,7 @@ export class CdkStep implements OnChanges {
284288 // want the form to be back to its initial state (see #29781). Submitted state is on the
285289 // individual directives, rather than the control, so we need to reset them ourselves.
286290 this . _childForms ?. forEach ( form => form . resetForm ?.( ) ) ;
287- this . stepControl . reset ( ) ;
291+ reset ( this . stepControl ) ;
288292 }
289293 }
290294
@@ -593,7 +597,7 @@ export class CdkStepper implements AfterContentInit, AfterViewInit, OnDestroy {
593597 . some ( step => {
594598 const control = step . stepControl ;
595599 const isIncomplete = control
596- ? control . invalid || control . pending || ! step . interacted
600+ ? isInvalid ( control ) || isPending ( control ) || ! step . interacted
597601 : ! step . completed ;
598602 return isIncomplete && ! step . optional && ! step . _completedOverride ( ) ;
599603 } ) ;
@@ -618,3 +622,29 @@ export class CdkStepper implements AfterContentInit, AfterViewInit, OnDestroy {
618622 return index > - 1 && ( ! this . steps || index < this . steps . length ) ;
619623 }
620624}
625+
626+ function isField ( value : StepControl ) : value is Field < unknown > {
627+ return typeof value === 'function' ;
628+ }
629+
630+ function isValid ( control : StepControl ) : boolean {
631+ return isField ( control ) ? control ( ) . valid ( ) : control . valid ;
632+ }
633+
634+ function isInvalid ( control : StepControl ) : boolean {
635+ // Note: it's a bit redundant to have both `isValid` and `isInvalid`. We need both, because
636+ // some internal apps mock out `invalid` specifically so `!valid` won't hit the mock.
637+ return isField ( control ) ? control ( ) . invalid ( ) : control . invalid ;
638+ }
639+
640+ function isPending ( control : StepControl ) : boolean {
641+ return isField ( control ) ? control ( ) . pending ( ) : control . pending ;
642+ }
643+
644+ function reset ( control : StepControl ) : void {
645+ if ( isField ( control ) ) {
646+ control ( ) . reset ( ) ;
647+ } else {
648+ control . reset ( ) ;
649+ }
650+ }
0 commit comments