Skip to content

Commit 1cb210b

Browse files
authored
fix(material/stepper): two-way binding for selectedIndex (#27232)
* fix(material/stepper): two-way binding for selectedIndex adds two-way binding for selectedIndex, previously stepper had selectionChange which include selectedIndex & other properties in it fixes #15627 * fixup! fix(material/stepper): two-way binding for selectedIndex
1 parent 6f79a99 commit 1cb210b

File tree

3 files changed

+48
-1
lines changed

3 files changed

+48
-1
lines changed

src/cdk/stepper/stepper.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,9 @@ export class CdkStepper implements AfterContentInit, AfterViewInit, OnDestroy {
321321
/** Event emitted when the selected step has changed. */
322322
@Output() readonly selectionChange = new EventEmitter<StepperSelectionEvent>();
323323

324+
/** Output to support two-way binding on `[(selectedIndex)]` */
325+
@Output() readonly selectedIndexChange: EventEmitter<number> = new EventEmitter<number>();
326+
324327
/** Used to track unique ID for each stepper component. */
325328
_groupId: number;
326329

@@ -526,6 +529,7 @@ export class CdkStepper implements AfterContentInit, AfterViewInit, OnDestroy {
526529
: this._keyManager.updateActiveItem(newIndex);
527530

528531
this._selectedIndex = newIndex;
532+
this.selectedIndexChange.emit(this._selectedIndex);
529533
this._stateChanged();
530534
}
531535

src/material/stepper/stepper.spec.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1571,6 +1571,35 @@ describe('MatStepper', () => {
15711571
expect(element.textContent).toContain('Step 3 content');
15721572
});
15731573
});
1574+
1575+
describe('stepper with two-way binding on selectedIndex', () => {
1576+
it('should update selectedIndex in component on navigation', () => {
1577+
const fixture = createComponent(StepperWithTwoWayBindingOnSelectedIndex);
1578+
fixture.detectChanges();
1579+
1580+
expect(fixture.componentInstance.index).toBe(0);
1581+
1582+
const stepHeaders = fixture.debugElement.queryAll(By.css('.mat-horizontal-stepper-header'));
1583+
1584+
let lastStepHeaderEl = stepHeaders[2].nativeElement;
1585+
lastStepHeaderEl.click();
1586+
fixture.detectChanges();
1587+
1588+
expect(fixture.componentInstance.index).toBe(2);
1589+
1590+
let middleStepHeaderEl = stepHeaders[1].nativeElement;
1591+
middleStepHeaderEl.click();
1592+
fixture.detectChanges();
1593+
1594+
expect(fixture.componentInstance.index).toBe(1);
1595+
1596+
let firstStepHeaderEl = stepHeaders[0].nativeElement;
1597+
firstStepHeaderEl.click();
1598+
fixture.detectChanges();
1599+
1600+
expect(fixture.componentInstance.index).toBe(0);
1601+
});
1602+
});
15741603
});
15751604

15761605
/** Asserts that keyboard interaction works correctly. */
@@ -2166,3 +2195,16 @@ class StepperWithLazyContent {
21662195
class HorizontalStepperWithDelayedStep {
21672196
renderSecondStep = false;
21682197
}
2198+
2199+
@Component({
2200+
template: `
2201+
<mat-stepper [(selectedIndex)]="index">
2202+
<mat-step label="One"></mat-step>
2203+
<mat-step label="Two"></mat-step>
2204+
<mat-step label="Three"></mat-step>
2205+
</mat-stepper>
2206+
`,
2207+
})
2208+
class StepperWithTwoWayBindingOnSelectedIndex {
2209+
index: number = 0;
2210+
}

tools/public_api_guard/cdk/stepper.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,13 +115,14 @@ export class CdkStepper implements AfterContentInit, AfterViewInit, OnDestroy {
115115
set selected(step: CdkStep | undefined);
116116
get selectedIndex(): number;
117117
set selectedIndex(index: NumberInput);
118+
readonly selectedIndexChange: EventEmitter<number>;
118119
readonly selectionChange: EventEmitter<StepperSelectionEvent>;
119120
_stateChanged(): void;
120121
_stepHeader: QueryList<CdkStepHeader>;
121122
readonly steps: QueryList<CdkStep>;
122123
_steps: QueryList<CdkStep>;
123124
// (undocumented)
124-
static ɵdir: i0.ɵɵDirectiveDeclaration<CdkStepper, "[cdkStepper]", ["cdkStepper"], { "linear": { "alias": "linear"; "required": false; }; "selectedIndex": { "alias": "selectedIndex"; "required": false; }; "selected": { "alias": "selected"; "required": false; }; "orientation": { "alias": "orientation"; "required": false; }; }, { "selectionChange": "selectionChange"; }, ["_steps", "_stepHeader"], never, false, never, false>;
125+
static ɵdir: i0.ɵɵDirectiveDeclaration<CdkStepper, "[cdkStepper]", ["cdkStepper"], { "linear": { "alias": "linear"; "required": false; }; "selectedIndex": { "alias": "selectedIndex"; "required": false; }; "selected": { "alias": "selected"; "required": false; }; "orientation": { "alias": "orientation"; "required": false; }; }, { "selectionChange": "selectionChange"; "selectedIndexChange": "selectedIndexChange"; }, ["_steps", "_stepHeader"], never, false, never, false>;
125126
// (undocumented)
126127
static ɵfac: i0.ɵɵFactoryDeclaration<CdkStepper, [{ optional: true; }, null, null]>;
127128
}

0 commit comments

Comments
 (0)