mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
fix(sidenav): scrollable instance not exposed when explicitly specifying content element (#11706)
This is a resubmit of #11517 which got in by accident. * 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.
This commit is contained in:
committed by
mmalerba
parent
e462f3d4bd
commit
441c98e838
@@ -5,6 +5,6 @@
|
||||
|
||||
<ng-content select="mat-drawer-content">
|
||||
</ng-content>
|
||||
<mat-drawer-content *ngIf="!_content" cdkScrollable>
|
||||
<mat-drawer-content *ngIf="!_content">
|
||||
<ng-content></ng-content>
|
||||
</mat-drawer-content>
|
||||
|
||||
@@ -16,6 +16,7 @@ import {A11yModule} from '@angular/cdk/a11y';
|
||||
import {PlatformModule} from '@angular/cdk/platform';
|
||||
import {ESCAPE} from '@angular/cdk/keycodes';
|
||||
import {dispatchKeyboardEvent} from '@angular/cdk/testing';
|
||||
import {CdkScrollable} from '@angular/cdk/scrolling';
|
||||
|
||||
|
||||
describe('MatDrawer', () => {
|
||||
@@ -497,6 +498,7 @@ describe('MatDrawerContainer', () => {
|
||||
DrawerContainerStateChangesTestApp,
|
||||
AutosizeDrawer,
|
||||
BasicTestApp,
|
||||
DrawerContainerWithContent,
|
||||
],
|
||||
});
|
||||
|
||||
@@ -719,6 +721,27 @@ describe('MatDrawerContainer', () => {
|
||||
expect(fixture.componentInstance.drawer.opened).toBe(false);
|
||||
}));
|
||||
|
||||
it('should expose a scrollable when the consumer has not specified drawer content',
|
||||
fakeAsync(() => {
|
||||
const fixture = TestBed.createComponent(DrawerContainerTwoDrawerTestApp);
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.componentInstance.drawerContainer.scrollable instanceof CdkScrollable)
|
||||
.toBe(true);
|
||||
}));
|
||||
|
||||
it('should expose a scrollable when the consumer has specified drawer content',
|
||||
fakeAsync(() => {
|
||||
const fixture = TestBed.createComponent(DrawerContainerWithContent);
|
||||
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(fixture.componentInstance.drawerContainer.scrollable instanceof CdkScrollable)
|
||||
.toBe(true);
|
||||
}));
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -901,3 +924,16 @@ class AutosizeDrawer {
|
||||
@ViewChild(MatDrawer) drawer: MatDrawer;
|
||||
fillerWidth = 0;
|
||||
}
|
||||
|
||||
|
||||
@Component({
|
||||
template: `
|
||||
<mat-drawer-container>
|
||||
<mat-drawer>Drawer</mat-drawer>
|
||||
<mat-drawer-content>Content</mat-drawer-content>
|
||||
</mat-drawer-container>
|
||||
`,
|
||||
})
|
||||
class DrawerContainerWithContent {
|
||||
@ViewChild(MatDrawerContainer) drawerContainer: MatDrawerContainer;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import {Directionality} from '@angular/cdk/bidi';
|
||||
import {coerceBooleanProperty} from '@angular/cdk/coercion';
|
||||
import {ESCAPE} from '@angular/cdk/keycodes';
|
||||
import {Platform} from '@angular/cdk/platform';
|
||||
import {CdkScrollable} from '@angular/cdk/scrolling';
|
||||
import {CdkScrollable, ScrollDispatcher} from '@angular/cdk/scrolling';
|
||||
import {DOCUMENT} from '@angular/common';
|
||||
import {
|
||||
AfterContentChecked,
|
||||
@@ -75,10 +75,14 @@ export function MAT_DRAWER_DEFAULT_AUTOSIZE_FACTORY(): boolean {
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
encapsulation: ViewEncapsulation.None,
|
||||
})
|
||||
export class MatDrawerContent implements AfterContentInit {
|
||||
export class MatDrawerContent extends CdkScrollable implements AfterContentInit {
|
||||
constructor(
|
||||
private _changeDetectorRef: ChangeDetectorRef,
|
||||
@Inject(forwardRef(() => MatDrawerContainer)) public _container: MatDrawerContainer) {
|
||||
@Inject(forwardRef(() => MatDrawerContainer)) public _container: MatDrawerContainer,
|
||||
elementRef: ElementRef<HTMLElement>,
|
||||
scrollDispatcher: ScrollDispatcher,
|
||||
ngZone: NgZone) {
|
||||
super(elementRef, scrollDispatcher, ngZone);
|
||||
}
|
||||
|
||||
ngAfterContentInit() {
|
||||
@@ -401,6 +405,7 @@ export class MatDrawer implements AfterContentInit, AfterContentChecked, OnDestr
|
||||
export class MatDrawerContainer implements AfterContentInit, DoCheck, OnDestroy {
|
||||
@ContentChildren(MatDrawer) _drawers: QueryList<MatDrawer>;
|
||||
@ContentChild(MatDrawerContent) _content: MatDrawerContent;
|
||||
@ViewChild(MatDrawerContent) _userContent: MatDrawerContent;
|
||||
|
||||
/** The drawer child with the `start` position. */
|
||||
get start(): MatDrawer | null { return this._start; }
|
||||
@@ -471,7 +476,9 @@ export class MatDrawerContainer implements AfterContentInit, DoCheck, OnDestroy
|
||||
readonly _contentMarginChanges = new Subject<{left: number|null, right: number|null}>();
|
||||
|
||||
/** Reference to the CdkScrollable instance that wraps the scrollable content. */
|
||||
@ViewChild(CdkScrollable) scrollable: CdkScrollable;
|
||||
get scrollable(): CdkScrollable {
|
||||
return this._userContent || this._content;
|
||||
}
|
||||
|
||||
constructor(@Optional() private _dir: Directionality,
|
||||
private _element: ElementRef,
|
||||
|
||||
@@ -109,8 +109,8 @@ The `<mat-sidenav>` can render in one of three different ways based on the `mode
|
||||
|
||||
| Mode | Description |
|
||||
|--------|-----------------------------------------------------------------------------------------|
|
||||
| `over` | Sidenav floats over the primary content, which is covered by a backdrop |
|
||||
| `push` | Sidenav pushes the primary content out of its way, also covering it with a backdrop |
|
||||
| `over` | Sidenav floats over the primary content, which is covered by a backdrop |
|
||||
| `push` | Sidenav pushes the primary content out of its way, also covering it with a backdrop |
|
||||
| `side` | Sidenav appears side-by-side with the main content, shrinking the main content's width to make space for the sidenav. |
|
||||
|
||||
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
|
||||
`CdkScrollable` instance through the `MatSidenavContainer`.
|
||||
|
||||
```ts
|
||||
class YourComponent {
|
||||
class YourComponent implements AfterViewInit {
|
||||
@ViewChild(MatSidenavContainer) sidenavContainer: MatSidenavContainer;
|
||||
|
||||
constructor() {
|
||||
ngAfterViewInit() {
|
||||
this.sidenavContainer.scrollable.elementScrolled().subscribe(() => /* react to scrolling */);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,10 +17,13 @@ import {
|
||||
Input,
|
||||
ViewEncapsulation,
|
||||
QueryList,
|
||||
ElementRef,
|
||||
NgZone,
|
||||
} from '@angular/core';
|
||||
import {MatDrawer, MatDrawerContainer, MatDrawerContent} from './drawer';
|
||||
import {matDrawerAnimations} from './drawer-animations';
|
||||
import {coerceBooleanProperty, coerceNumberProperty} from '@angular/cdk/coercion';
|
||||
import {ScrollDispatcher} from '@angular/cdk/scrolling';
|
||||
|
||||
|
||||
@Component({
|
||||
@@ -38,8 +41,11 @@ import {coerceBooleanProperty, coerceNumberProperty} from '@angular/cdk/coercion
|
||||
export class MatSidenavContent extends MatDrawerContent {
|
||||
constructor(
|
||||
changeDetectorRef: ChangeDetectorRef,
|
||||
@Inject(forwardRef(() => MatSidenavContainer)) container: MatSidenavContainer) {
|
||||
super(changeDetectorRef, container);
|
||||
@Inject(forwardRef(() => MatSidenavContainer)) container: MatSidenavContainer,
|
||||
elementRef: ElementRef<HTMLElement>,
|
||||
scrollDispatcher: ScrollDispatcher,
|
||||
ngZone: NgZone) {
|
||||
super(changeDetectorRef, container, elementRef, scrollDispatcher, ngZone);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user