Skip to content

Commit 436ac7d

Browse files
crisbetoandrewseguin
authored andcommitted
fix(sidenav): scrollable instance not exposed when explicitly specifying content element (#11517)
* Fixes the `MatSidenavContainer.scrollable` being undefined if the consumer has set the `mat-sidenav-content` themselves. The issue comes from the fact that we only query for scrollables inside the drawer's own view, but not inside the projected content. * Fixes the example in the sidenav docs accessing the scrollable too early. Fixes #10884. BREAKING CHANGE: the constructor signature of the `MatDrawerContent` and `MatSidenavContent` has changed.
1 parent 59554c4 commit 436ac7d

File tree

5 files changed

+60
-11
lines changed

5 files changed

+60
-11
lines changed

src/lib/sidenav/drawer-container.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@
55

66
<ng-content select="mat-drawer-content">
77
</ng-content>
8-
<mat-drawer-content *ngIf="!_content" cdkScrollable>
8+
<mat-drawer-content *ngIf="!_content">
99
<ng-content></ng-content>
1010
</mat-drawer-content>

src/lib/sidenav/drawer.spec.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {A11yModule} from '@angular/cdk/a11y';
1515
import {PlatformModule} from '@angular/cdk/platform';
1616
import {ESCAPE} from '@angular/cdk/keycodes';
1717
import {dispatchKeyboardEvent} from '@angular/cdk/testing';
18+
import {CdkScrollable} from '@angular/cdk/scrolling';
1819

1920

2021
describe('MatDrawer', () => {
@@ -496,6 +497,7 @@ describe('MatDrawerContainer', () => {
496497
DrawerContainerStateChangesTestApp,
497498
AutosizeDrawer,
498499
BasicTestApp,
500+
DrawerContainerWithContent,
499501
],
500502
});
501503

@@ -692,6 +694,27 @@ describe('MatDrawerContainer', () => {
692694
expect(fixture.componentInstance.drawer.opened).toBe(false);
693695
}));
694696

697+
it('should expose a scrollable when the consumer has not specified drawer content',
698+
fakeAsync(() => {
699+
const fixture = TestBed.createComponent(DrawerContainerTwoDrawerTestApp);
700+
701+
fixture.detectChanges();
702+
703+
expect(fixture.componentInstance.drawerContainer.scrollable instanceof CdkScrollable)
704+
.toBe(true);
705+
}));
706+
707+
it('should expose a scrollable when the consumer has specified drawer content',
708+
fakeAsync(() => {
709+
const fixture = TestBed.createComponent(DrawerContainerWithContent);
710+
711+
fixture.detectChanges();
712+
713+
expect(fixture.componentInstance.drawerContainer.scrollable instanceof CdkScrollable)
714+
.toBe(true);
715+
}));
716+
717+
695718
});
696719

697720

@@ -874,3 +897,16 @@ class AutosizeDrawer {
874897
@ViewChild(MatDrawer) drawer: MatDrawer;
875898
fillerWidth = 0;
876899
}
900+
901+
902+
@Component({
903+
template: `
904+
<mat-drawer-container>
905+
<mat-drawer>Drawer</mat-drawer>
906+
<mat-drawer-content>Content</mat-drawer-content>
907+
</mat-drawer-container>
908+
`,
909+
})
910+
class DrawerContainerWithContent {
911+
@ViewChild(MatDrawerContainer) drawerContainer: MatDrawerContainer;
912+
}

src/lib/sidenav/drawer.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {Directionality} from '@angular/cdk/bidi';
1111
import {coerceBooleanProperty} from '@angular/cdk/coercion';
1212
import {ESCAPE} from '@angular/cdk/keycodes';
1313
import {Platform} from '@angular/cdk/platform';
14-
import {CdkScrollable} from '@angular/cdk/scrolling';
14+
import {CdkScrollable, ScrollDispatcher} from '@angular/cdk/scrolling';
1515
import {DOCUMENT} from '@angular/common';
1616
import {
1717
AfterContentChecked,
@@ -75,10 +75,14 @@ export function MAT_DRAWER_DEFAULT_AUTOSIZE_FACTORY(): boolean {
7575
changeDetection: ChangeDetectionStrategy.OnPush,
7676
encapsulation: ViewEncapsulation.None,
7777
})
78-
export class MatDrawerContent implements AfterContentInit {
78+
export class MatDrawerContent extends CdkScrollable implements AfterContentInit {
7979
constructor(
8080
private _changeDetectorRef: ChangeDetectorRef,
81-
@Inject(forwardRef(() => MatDrawerContainer)) public _container: MatDrawerContainer) {
81+
@Inject(forwardRef(() => MatDrawerContainer)) public _container: MatDrawerContainer,
82+
elementRef: ElementRef<HTMLElement>,
83+
scrollDispatcher: ScrollDispatcher,
84+
ngZone: NgZone) {
85+
super(elementRef, scrollDispatcher, ngZone);
8286
}
8387

8488
ngAfterContentInit() {
@@ -401,6 +405,7 @@ export class MatDrawer implements AfterContentInit, AfterContentChecked, OnDestr
401405
export class MatDrawerContainer implements AfterContentInit, DoCheck, OnDestroy {
402406
@ContentChildren(MatDrawer) _drawers: QueryList<MatDrawer>;
403407
@ContentChild(MatDrawerContent) _content: MatDrawerContent;
408+
@ViewChild(MatDrawerContent) _userContent: MatDrawerContent;
404409

405410
/** The drawer child with the `start` position. */
406411
get start(): MatDrawer | null { return this._start; }
@@ -471,7 +476,9 @@ export class MatDrawerContainer implements AfterContentInit, DoCheck, OnDestroy
471476
readonly _contentMarginChanges = new Subject<{left: number|null, right: number|null}>();
472477

473478
/** Reference to the CdkScrollable instance that wraps the scrollable content. */
474-
@ViewChild(CdkScrollable) scrollable: CdkScrollable;
479+
get scrollable(): CdkScrollable {
480+
return this._userContent || this._content;
481+
}
475482

476483
constructor(@Optional() private _dir: Directionality,
477484
private _element: ElementRef,

src/lib/sidenav/sidenav.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,8 @@ The `<mat-sidenav>` can render in one of three different ways based on the `mode
109109

110110
| Mode | Description |
111111
|--------|-----------------------------------------------------------------------------------------|
112-
| `over` | Sidenav floats over the primary content, which is covered by a backdrop |
113-
| `push` | Sidenav pushes the primary content out of its way, also covering it with a backdrop |
112+
| `over` | Sidenav floats over the primary content, which is covered by a backdrop |
113+
| `push` | Sidenav pushes the primary content out of its way, also covering it with a backdrop |
114114
| `side` | Sidenav appears side-by-side with the main content, shrinking the main content's width to make space for the sidenav. |
115115

116116
If no `mode` is specified, `over` is used by default.
@@ -186,10 +186,10 @@ To react to scrolling inside the `<mat-sidenav-container>`, you can get a hold o
186186
`CdkScrollable` instance through the `MatSidenavContainer`.
187187

188188
```ts
189-
class YourComponent {
189+
class YourComponent implements AfterViewInit {
190190
@ViewChild(MatSidenavContainer) sidenavContainer: MatSidenavContainer;
191191

192-
constructor() {
192+
ngAfterViewInit() {
193193
this.sidenavContainer.scrollable.elementScrolled().subscribe(() => /* react to scrolling */);
194194
}
195195
}

src/lib/sidenav/sidenav.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,13 @@ import {
1717
Input,
1818
ViewEncapsulation,
1919
QueryList,
20+
ElementRef,
21+
NgZone,
2022
} from '@angular/core';
2123
import {MatDrawer, MatDrawerContainer, MatDrawerContent} from './drawer';
2224
import {matDrawerAnimations} from './drawer-animations';
2325
import {coerceBooleanProperty, coerceNumberProperty} from '@angular/cdk/coercion';
26+
import {ScrollDispatcher} from '@angular/cdk/scrolling';
2427

2528

2629
@Component({
@@ -38,8 +41,11 @@ import {coerceBooleanProperty, coerceNumberProperty} from '@angular/cdk/coercion
3841
export class MatSidenavContent extends MatDrawerContent {
3942
constructor(
4043
changeDetectorRef: ChangeDetectorRef,
41-
@Inject(forwardRef(() => MatSidenavContainer)) container: MatSidenavContainer) {
42-
super(changeDetectorRef, container);
44+
@Inject(forwardRef(() => MatSidenavContainer)) container: MatSidenavContainer,
45+
elementRef: ElementRef<HTMLElement>,
46+
scrollDispatcher: ScrollDispatcher,
47+
ngZone: NgZone) {
48+
super(changeDetectorRef, container, elementRef, scrollDispatcher, ngZone);
4349
}
4450
}
4551

0 commit comments

Comments
 (0)