Angular Material 提供了两组组件,用以给主要内容添加一些可折叠的附属内容(通常是导航,但也可以是任何内容)。它们就是侧边栏(sidenav)和抽屉(drawer)组件。
侧边栏组件旨在为全屏应用添加附属内容。要建立侧边栏,我们需要用到三个组件:<mat-sidenav-container>
用来为主要内容和侧边栏提供一个结构容器;<mat-sidenav-content>
用来表示主要内容,而 <mat-sidenav>
用于表示附属内容。
抽屉组件旨在给应用中的一小部分添加附属内容。这可以使用 <mat-drawer-container>
、<mat-drawer-content>
和 <mat-drawer>
来实现,它们分别是各个侧边栏组件的等价物。侧边栏会把应用的附属内容作为整体添加进来,而抽屉只在为应用中的一小部分添加附属内容。
它们所支持的大部分特性都一样,但抽屉不支持固定定位方式。
无论主内容还是附属内容,都应该放在 <mat-sidenav-container>
的内部,而那些你不希望被侧边栏影响到的内容(比如头或脚),可以放在该容器的外部。
附属内容应该包装在 <mat-sidenav>
元素中。它的 position
属性可以指定主内容该放在附属内容的哪一端,它可以是 start
或 end
,在从左到右书写的语言中下,分别表示把主内容放在附属内容的左侧或右侧。
如果没有指定 position
,则其默认值是 start
。
<mat-sidenav-container>
最多可以拥有两个 <mat-sidenav>
元素,但每一侧只能有一个。
<mat-sidenav>
必须作为 <mat-sidenav-container>
的直属子节点出现。
主要内容应该包裹在 <mat-sidenav-content>
中,如果没有为 <mat-sidenav-container>
指定 <mat-sidenav-content>
,则会隐式创建一个,并把 <mat-sidenav-container>
中除了 <mat-sidenav>
元素之外的内容都放进去。
下面是正确使用侧边栏布局的例子:
<!-- Creates a layout with a left-positioned sidenav and explicit content. -->
<mat-sidenav-container>
<mat-sidenav>Start</mat-sidenav>
<mat-sidenav-content>Main</mat-sidenav-content>
</mat-sidenav-container>
<!-- Creates a layout with a left and right sidenav and implicit content. -->
<mat-sidenav-container>
<mat-sidenav>Start</mat-sidenav>
<mat-sidenav position="end">End</mat-sidenav>
<section>Main</section>
</mat-sidenav-container>
<!-- Creates an empty sidenav container with no sidenavs and implicit empty content. -->
<mat-sidenav-container></mat-sidenav-container>
下面是错误使用侧边栏布局的例子:
<!-- Invalid because there are two `start` position sidenavs. -->
<mat-sidenav-container>
<mat-sidenav>Start</mat-sidenav>
<mat-sidenav position="start">Start 2</mat-sidenav>
</mat-sidenav-container>
<!-- Invalid because there are multiple `<mat-sidenav-content>` elements. -->
<mat-sidenav-container>
<mat-sidenav-content>Main</mat-sidenav-content>
<mat-sidenav-content>Main 2</mat-sidenav-content>
</mat-sidenav-container>
<!-- Invalid because the `<mat-sidenav>` is outside of the `<mat-sidenav-container>`. -->
<mat-sidenav-container></mat-sidenav-container>
<mat-sidenav></mat-sidenav>
这些规则也同样适用于抽屉组件。
<mat-sidenav>
可以使用 open()
、close()
和 toggle()
方法来打开或关闭。
它们都会返回一个 Promise<boolean>
,当侧边栏打开之后它会解析为 true
,关闭之后解析为 false
。
这些打开状态也可以在模板中使用 opened
属性进行设置。该属性支持双向绑定。
<mat-sidenav>
也支持一些输出属性:(opened)
表示刚刚打开,(closed)
表示刚刚关闭。
所有这些属性和方法也同样可用在 <mat-drawer>
上。
<mat-sidenav>
可以根据其 mode
属性的值以三种方式之一进行渲染。
模式 | 描述 |
---|---|
over |
侧边栏浮动在主要内容之上,主要内容被遮罩覆盖 |
push |
侧边栏将主要内容推开,同时用遮罩覆盖它 |
side |
侧边栏与主内容并排显示,缩减主内容的宽度为侧边栏腾出空间。 |
如果没有指定 mode
,则默认为 over
。
侧边栏的 over
和 push
模式默认会显示一个背景,但 side
模式不会。这可以通过 mat-sidenav-container
上的
hasBackdrop
属性进行设置。显式把 hasBackdrop
设置为 true
或 false
将会为侧边栏改写默认的背景可见性,而不管处在什么模式下。不设置该属性或把它设置为 null
将会使用每种模式下默认的背景可见性。
<mat-drawer>
也同样支持这些模式和选项。
点击背景或按下 Esc 键通常会关闭侧边栏。
不过,可以通过设置 <mat-sidenav>
或 <mat-drawer>
上的 disableClose
属性来禁用这种自动关闭的行为。
可以通过给 <mat-sidenav>
添加 keydown
监听器来定制 Esc 处理器。
可以通过 <mat-sidenav-container>
的输出属性 (backdropClick)
来定制点击背景的处理器。
默认情况下,Material 只会在一些关键时刻(打开、窗口调整大小、模式改变)测量和调整容器的大小,以避免布局颠簸。
但是在某些情况下这可能会有问题。如果你希望在打开抽屉时更改其宽度,可以使用 autosize
选项来告诉 Material 继续测量它。
注意,使用该选项时应该风险自担,因为它可能会导致性能问题。
默认情况下,<mat-sidenav>
和 <mat-drawer>
应该自适应其内容的尺寸。不过也可以通过 CSS 来显式指定宽度:
mat-sidenav {
width: 200px;
}
避免使用基于百分比的宽度,因为 resize
事件尚未支持它。
<mat-sidenav>
只支持固定定位方式(<mat-drawer>
不限)。它可以通过设置 fixedInViewport
属性进行启用。
另外,还可以通过 fixedTopGap
和 fixedBottomGap
来设置顶部和底部的空白。这些属性可以接受一个像素值来指定要加到顶部或底部的空白尺寸。
侧边栏通常要在移动端和桌面端提供不同的行为。在桌面端,只允许内容区滚动是合理的;在移动端,你通常会希望滚动整个 body
,这样就能让浏览器自动隐藏地址栏。侧边栏可以使用 CSS 来设置样式,以针对不同类型的设备进行调整。
要响应 <mat-sidenav-container>
内部的滚动事件,你可以通过 MatSidenavContainer
来获取一个底层的 CdkScrollable
实例。
class YourComponent implements AfterViewInit {
@ViewChild(MatSidenavContainer) sidenavContainer: MatSidenavContainer;
ngAfterViewInit() {
this.sidenavContainer.scrollable.elementScrolled().subscribe(() => /* react to scrolling */);
}
}
<mat-sidenav>
和 <mat-sidenav-content>
都应该根据它们的上下文给出一个合适的 role
属性。
比如,包含到其它页面的链接的 <mat-sidenav>
可以标记为 role="navigation"
,而包含目录的则应该标记为 role="directory"
。
如果没有什么特别的角色来描述这个侧边栏,建议使用 role="region"
。
同样,<mat-sidenav-content>
也应该基于其包含的内容来指定角色。如果它表示页面的主要内容,就应该标记为 role="main"
。
如果没办法指定合理的角色,同样可以用 role="region"
作为回退值。
sidenav 能够捕获焦点。此功能在 push
和 over
模式下开启,在 side
模式下则被关闭。你可以通过输入属性 autoFocus
来改变其默认行为。
默认情况下,一旦打开,其中的第一个可接收焦点的元素就会收到焦点。如果你想让另一个元素获得焦点,可以在它上面添加 cdkFocusInitial
属性。
如果在指定容器的同一个 position
有多个侧边栏或抽屉,就会抛出本错误。
由于 position
属性默认为 start
,所以出现该问题可能只是因为你忘了给 end
侧边栏标记上 position="end"
。