MDC-based Angular Material组件迁移

发布时间 2023-08-08 19:33:25作者: eagle.supper

1. 前言

在 Angular Material v15 中,许多组件已基于官方的 Web Material Design Components (MDC) 进行了重构。 以下导入的组件已被重构:

Import path Summary of changes
@angular/material/autocomplete Style changes only
@angular/material/button Style changes, API changes
@angular/material/card Style changes only
@angular/material/checkbox Style changes, changes to event behavior
@angular/material/chips Complete rewrite
@angular/material/core Style changes only
@angular/material/dialog Style changes, changes to change detection behavior
@angular/material/form-field Style changes, some appearances removed, API changes
@angular/material/input Style changes only
@angular/material/list Style changes, API changes
@angular/material/menu Style changes, API changes
@angular/material/paginator Style changes only
@angular/material/progress-bar Style changes only
@angular/material/progress-spinner Style changes only
@angular/material/radio Style changes only
@angular/material/select Style changes only
@angular/material/slide-toggle Style changes only
@angular/material/slider Complete rewrite
@angular/material/snack-bar Style changes, API changes
@angular/material/table Style changes only
@angular/material/tabs Style changes, API changes
@angular/material/tooltip Style changes only

与旧的实现相比,重构的组件具有多种优势,包括:

  • 改进的可访问性
  • 更好地遵守Material Design规范
  • 由于基于通用基础设施,Material Design 规范未来版本的被采用速度更快

2. 发生了哪些变化?

新组件具有不同的内部 DOM 和 CSS 样式。 然而,大多数 TypeScript API 和新组件的组件/指令选择器都尽可能接近旧的实现。 这使得迁移应用程序并使其与新组件一起运行变得简单。

由于新的 DOM 和 CSS,您可能会发现应用程序中的某些样式需要调整,特别是当您的 CSS 覆盖任何迁移组件上的内部元素的样式时。

为了与 MDC 集成,有一些组件对其 API 进行了较大的更改。 这些组件包括:

  • form-field
  • chips
  • slider
  • list

有关所有组件更改请参阅变更完整列表

每个新组件的旧实现现已弃用,但仍可通过“legacy”导入使用。 例如,您可以导入旧的 mat-button 实现,可以通过导入旧的按钮模块来使用。

import {MatLegacyButtonModule} from '@angular/material/legacy-button';

3. 如何迁移

您可以通过运行 Angular Material 的自动重构工具来开始迁移。该工具以Angular Schematic 的形式实现,可将大部分代码更新为新的组件版本。虽然一些后续工作是必要的,但您可以通过遵循以下最佳实践来减少手动工作量:

通过确保您的应用程序在迁移之前遵循良好实践,您可以减少所需的手动工作量

  • 尽可能避免覆盖Angular Material元素的内部css样式。如果您发现自己经常覆盖Angular Material元素的样式,请考虑使用专为更多自定义样式而设计的组件,例如 Angular CDK中提供的组件。
  • 在测试中使用component harnesses与 Angular Material 组件交互,而不是检查组件的内部元素、属性或方法。使用component harnesses使您的测试更容易理解并且对 Angular Material 的变化更加稳健。

3.1. 更新至 Angular Material v15

Angular Material 包含一个工作流工具(Schematic),可帮助迁移应用程序以使用新的基于 MDC 的组件。首先,请将您的应用程序升级到 Angular Material 15。

ng update @angular/material@15

作为此更新的一部分,将运行Schematic以自动移动您的应用程序以使用包含旧组件实现的“legacy”导入。这提供了一条让您的应用程序在 v15 上运行的快速路径,只需最少的手动更改。

3.2. 使用迁移工具

当使用上面的命令升级到Angular Material v15之后, 我们可以运行迁移工具从旧的Angular material组件迁移到新的基于MDC的组件.


ng generate @angular/material:mdc-migration

这条指令将会更新TypeScript文件, 样式单, 和html模板, 使用新的组件, 这条指令将尽可能多的自动转换, 但是后续人需要手工更新一些遗漏的地方.

3.2.1. 执行部分更新

根据应用程序的大小和复杂性,您可能希望一次迁移单个组件或一部分组件,而不是一次迁移所有组件。
您可能还想一次迁移一个模块,而不是一次性完成全部迁移。您可以在同一应用程序中同时使用旧实现和新实现,只要它们不在同一NgModule中即可。
迁移脚本将指引您输入要迁移的目录和组件。

3.3. 检查迁移工具遗留下TODO备注

某些情况下, 迁移工具不能自动更新你的部分代码, 它会在代码前加上一些TODO备注方便后续手段更新. 这些TODO备注的样式如下所示:

// TODO(mdc-migration): ...

在您的IDE中使用关键字"TODO(mdc-migration):"即可以找打所有这些备注.

3.4. 验证应用

运行完迁移工具并且手动处理完这些TODO备注后, 我们就可以着手验证我的应用程序是否依然能正常工作了.

运行自动化测试确保测试都能通过. 有些测试依赖旧组件的Internal DOM结构和异步时间详细信息, 这些测试可能需要修改. 如果您打算修改一些测试, 考虑使用component harnesses, 这样可以使得您的测试更加健壮.

将应用运行起来并验证新的组件是不是看起来是否有差异. 因为一些处理内部DOM和样式的代码, 你可能需要重新调整一些样式.

到这里迁移工作大致已经完成. 下面是从旧的Angular Material组件迁移到MDC-based组件的完整变更列表, 供您参考.

4. 完整变更列表

4.1. 库范围的更改

  • 组件大小、颜色、间距、阴影和动画都会在各个方面略有变化。这些更改通常会更加符合Material Design规范和可访问性。

  • 所有组件的 DOM 结构都已更改,以改进可访问性并更好地遵循Material Design规范。

  • 应用于组件的CSS类使用mat-mdc-前缀,而以前它只是一个mat-前缀。与旧实现中的元素大致对应的元素被赋予了相同的类名(前缀除外)。例如,按钮的主机类是 mat-mdc-button 而不是 mat-button。但是,并非先前实现中的所有元素在新实现中都具有等效元素。

  • 与 mat-typography 关联的类样式不再自动生成。你必须使用mat.typography-hierarchy mixin来包含它们。

4.2. 主题

mat.define-typeography-config定义的默认排版级别已更新,以反映对Material Design规范的更改。
现在,所有组件都具有可主题化的(themeable)密度。默认情况下,当包含theme mixin时,将包含默认密度Level(0)的样式。


@use '@angular/material' as mat;

$theme: mat.define-light-theme((
  color: ...
));

// Adds density level 0 styles
@include mat.all-component-themes($theme);

如果您喜欢不同的默认密度级别,可以在主题配置中进行设置:


$theme: mat.define-light-theme((
  color: ...,
  density: -1
));

4.3. Autocomplete

  • 长选项现在换行而不是截断。
  • 选项高度不再限制在48像素。
  • 选项列表现在有一个8px的顶部和底部填充。
  • 选项列表现在具有打开和关闭时的动画。

4.4. Button

图标按钮的高度和宽度是48px,而不是40px。

状态颜色(悬停、聚焦、活动)略有不同,以提高文本对比度。

字母间距为1.25像素,而非正常值。

FAB支持具有extended输入属性的文本。

主题mixins被分为三个单独的mixins:

普通按钮(默认、凸起、stroked、平面):mat.mdc-button-theme

图标按钮:mat.mdc-Icon-button-theme

FAB:mat.mdc-fab-theme

按钮内容中的图标位于按钮文本之前。添加iconPositionEnd属性以将它们放置在按钮文本之后。

按钮内容中的图标继承文本字体大小。只有图标而没有文本的按钮无法正确对齐(这不适用于图标按钮)

4.5. Card

默认情况下,mat-card不应用任何内部填充。相反,这种填充是在卡片内容区域上定义的:<mat-card-content>、<mat-card-header>和<mat-card-action>。
<mat-card-content>不再设置任何排版样式,用户可以自由地将任何对其应用有意义的排版样式添加到<mat-card-content>本身或任何子元素中(视情况而定)。例如:

@use '@angular/material' as mat;
@include mat.typography-hierarchy();
<mat-card>
  <mat-card-content class="mat-body-1">...</mat-card-content>
</mat-card>

4.6. Checkbox

在新的MDC-based Angular Material组件中, 复选框的行为发生了一些变化. 在单击复选框时, 事件直接在原生复选框元素上触发, 而不是在填充div上触发. 原生复选框在单击事件中调用preventDefault时会有奇怪的行为,因此用户不应在单击时调用preventDefault。

复选框触摸目标更大,现在是40px,而不是16px,这更容易访问。确保在布局中留出足够的空间,以便触摸目标不会与其他组件重叠。如果您不关心可访问性,您可以通过使用 density(-1) 作为复选框来匹配以前的大小。


@use '@angular/material' as mat;
@include mat.checkbox-density(-1);

由于复选框颜色变更为基于应用程序主题,复选框颜色可能会更改为白色或黑色。以前,复选框的颜色将设置为主题的背景色。对于MDC,它是由白色还是黑色相对于原色具有最大的对比度来决定的。

对焦状态稍暗,提高对比度。

文本样式不是继承的;您需要专门针对复选框的标签来覆盖typography属性。

check 或者uncheck复选框时,波纹将保持可见,而不是动画消失。

4.7. Chips

Chips曾经是指令,但现在它们是组件。 这意味着它们不能再应用于其他组件。

Chips组件已分为多个变体,与更适合可访问性的交互模式相对应。 原本的 mat-chip-list 使用 role="listbox",但这种交互模式并不适合所有用例。 新Chips具有:

  • <mat-chip-listbox> 和 <mat-chip-option> - 这是最接近之前的交互模式。 这是唯一支持Chips选择状态的变体。 此模式与Material Design规范中指定的filter chips模式一致。 当您希望用户从选项列表中选择一个或多个值时,应使用此模式。

  • <mat-chip-grid> 和 <mat-chip-row> - 此模式应用于任何文本输入 + Chips交互。

  • <mat-chip-set> 和 <mat-chip> - 此变体没有可访问模式,并假设将在应用程序级别应用一个模式。 这允许应用程序通过Chips视觉效果实现自定义模式。

迁移工具始终将旧版 <mat-chip-list> 更改为 <mat-chip-listbox> 以最大程度地减少前后差异。 您应该根据具体情况单独考虑更改为 <mat-chip-grid> 或 <mat-chip-set>。 有关更多根据用例选择适当组件的指南,请参阅Chips交互模式

4.8. Dialog

.mat-dialog-container 不再包含 24px 填充。 但是可以使用内部对话框指令负责添加正确的填充。 如果您的对话框不使用 <mat-dialog-content> 等任何指令,这一点变化将会很明显。

mat-dialog-content 使用 Material Design 规范指定的字体设置,其中包括相当宽敞的行高。 如果您的对话框是信息密集型对话框, 在这些设置下会看起来不太好,您可以避免使用 <mat-dialog-content> 并仅使用带有自定义填充的 div,或者使用可与 mat.mdc-dialog-typography mixin 一起应用的自定义排版设置。

如果在旧对话框触发了额外的更改检测,在迁移时可能需要手动修复的更改检测问题。

4.9. Form Field

“legacy”和“standard”表单字段外观设置不再存在,因为它们已从 Material Design 规范中删除。

如果未指定标签,则删除的“legacy”外观会将placeholder提升为浮动标签。 如果之前未提供,则所有较新的外观设置都需要显式指定 <mat-label>。 此更改解决了不交替使用标签和占位符的可访问性最佳实践。

默认情况下,MatFormField 仍然在字段下方保留一行空间用于提示或错误文本。 然而,有一个新选项@Input() subscriptSizing: 'fixed'|'dynamic'。 当此设置设置为fixed(默认)时,表单字段在布局中保留足够的空间来显示一行提示或错误文本。 当设置为动态时,表单字段会扩展和收缩布局所需的空间量来显示的错误/提示。

<mat-hint> 内的文本更大、更暗,以符合 W3C text准则。

虽然以前的表单字段具有单个前缀指令 (matPrefix) 和单个后缀指令 (matSuffix),但基于 MDC 的表单字段区分与输入文本基线对齐的文本前缀/后缀和图标前缀 /后缀在表单字段中居中对齐。 使用 matTextPrefix 或 matTextSuffix 指示文本前缀/后缀,使用 matIconPrefix 或 matIconSuffix 指示图标前缀/后缀。 旧的 matSuffix 和 matPrefix API 的行为类似于图标,尽管它们现在已被弃用。

floatLabel 输入不再接受“never”。 floatLabel="never" 仅并旧表单字段外观支持。 它用于实现行为类似于占位符的浮动标签。 如果您需要此行为,请改用 <input> 上的占位符属性。

自定义表单字段控件可能需要调整其样式,因为表单字段 周围的DOM 和样式可能已经改变了。

4.10. Input

MatInput 必须位于 <mat-form-field> 内。 以前,如果页面在其他地方加载了表单字段样式,则可以使用不带表单字段的 <input matInput>。

基于 MDC 的 MatInput 会隐藏与 <input matInput type="date"> 关联的原生日历选择器指示符,如果您希望此指示符在您的输入中显示,请使用以下样式:


input.mat-mdc-input-element::-webkit-calendar-picker-indicator {
  display: block;
}

4.11. List

API已经过修改,以支持文本换行,并更好地与Material Design规范集成。
以前,列表项通常是使用matLine指令创建多个span元素。每个span生成一行,第一行成为主要行。在新的API中,matLine指令被拆分为两个更精细、更有意义的指令:

  • matListItemTitle
  • matListItemLine

matListItemLine之外的文本(所谓的“无范围内容”)将生成额外的行。


<mat-list-item>
  <span matListItemTitle>Title</span>
  Second line
</mat-list-item>

该列表会自动推断文本内容的行数。例如,在上面的代码段中,列表项为两行呈现空间。使用新的API,可以在<mat-list-item>上设置明确的行数,以手动控制换行。

<mat-list-item lines="3">
  <span matListItemTitle>Title</span>
  This text will wrap into the third line. Space for three lines is acquired by the
  list item.
</mat-list-item>

请注意,matListItemTitle或matListItemLine中的文本不会换行。根据提供的行数,只有无范围内容才会占用剩余空间。

除了行的组成方式不同之外,其他一些指令也被重命名为使用更明确的命名:

matListIcon变更为matListItemIcon

matListAvatar变更为matListItemAvatar
最后,还有一个新指令(matListItemMeta)可用于将内容放入列表项的meta section(通常是列表项的末尾)。列表项中以前无范围内容被放入meta section。

List建议的迁移步骤如下:

将第一个matLine更改为matListItemTitle

将所有其他matLine更改为matListItemLine

将所有matListIcon更改为matListItemIcon

将所有matListAvatar更改为matListItemAvatar

将所有无范围内容(matLine之外的内容)包装在matListItemMeta容器中。

5. Menu

无论DOM中的顺序如何,菜单项的图标都会出现在文本之前。
如果您有一段内容(如<img>)要用来代替<mat-icon>,请使用ngProjectAs=“mat-icon”将其投影到图标槽中。
如果你需要你的图标出现在项目的末尾(规范不正式支持),你可以将文本和图标包装在一个span中,例如:

<span>
  <span>Text</span>
  <mat-icon>end_icon</mat-icon>
</span>

菜单项中的文本换行,而不是用省略号隐藏。

菜单项高度不再限制在48像素。

菜单标高从标高8开始,而不是从标高4开始。

5.1. Option / Optgroup

长选项现在换行,而不是用省略号截断。

选项高度不再限制在48像素。

5.2. Paginator

mat-paginator内部的表单字段仅支持新表单字段提供的外观选项(fill和outline)。

5.3. Progress Bar

内部元素的可见性现在设置为visible。设置visibility: hidden将不再隐藏所有内部元素。如果要隐藏内部元素,使用opacity:0、display:none设置样式,或者使用ngIf将其完全删除。

高度始终设置为4px,并且使用高度样式不会变矮或变高。

5.4. Progress Spinner

Host element的display样式不再是可能影响布局的display: block。若要解决布局问题,请将display:block添加回元素。

5.5. Radio

单选按钮标签不再是宽度:100%。这有助于防止用户在单击页面上空白时意外选中单选按钮。

标签更小,离单选按钮更远,以符合“Material Design”规范。

触控目标现在更大,更容易触碰。确保在布局中留出足够的空间,以便触摸目标不会与其他组件重叠。如果你不关心可访问性,你可以通过对单选按钮使用density-1来匹配以前的尺寸。


@use '@angular/material' as mat;
@include mat.radio-density(-1);

5.6. Select

MatSelect不再将下拉菜单中的选定选项与trigger text对齐。

长选项现在换行而不是截断。

选项高度不再限制在48像素。

选项列表现在有一个8px的顶部和底部填充。

选项列表动画已更改。

以前,下拉菜单的宽度可以比父form-field宽,但现在下拉菜单与父form-field的宽度相同

5.7. Slide Toggle

为了提高可访问性,基于 MDC 的版本使用 <button role="switch"> 来表示切换,而不是 <input type="checkbox">。 滑动切换将不再响应原生表单验证。 考虑使用表单验证的替代方法。

触摸目标更大且更容易触及。 请务必在布局中留出足够的空间,以便触摸目标不会与其他组件重叠。 如果您不关心可访问性,您可以通过使用滑动切换的密度 -1 来匹配以前的尺寸。

@use '@angular/material' as mat;
@include mat.slide-toggle-density(-1);

标签更靠近滑动切换按钮

5.8. Slider

Slider现在可以与移动设备屏幕阅读器配合使用。

滑块模板API已从单个<mat-slider>元素更改为包含一个或两个<input>元素(取决于滑块应为标准滑块还是范围滑块)的<mat-srider>元素。例如。

<!-- Single slider -->
  <mat-slider>
    <input matSliderThumb>
  </mat-slider>

  <!-- Range slider -->
  <mat-slider>
    <input matSliderStartThumb>
    <input matSliderEndThumb>
  </mat-slider>

<mat-slider>上的新的discrete特性现在控制滑块是否具有tick marks和value indicator提示。它取代了thumbLabel。


<!-- Before -->
<mat-slider thumbLabel></mat-slider>

<!-- After -->
<mat-slider discrete>
  <input matSliderThumb>
</mat-slider>

tickInterval属性已被删除。要切换到新的API,请使用showTickMarks创建带有刻度线的滑块,刻度线的间隔将与滑块的步长相匹配。tickInterval属性正在考虑在将来的版本中重新添加。


<!-- Before -->
<mat-slider tickInterval="5" step="5"></mat-slider>

<!-- After -->
<mat-slider step="5" showTickMarks>
  <input matSliderThumb>
</mat-slider>

displayValue属性已被删除。控制数值指示器文本的建议替代方案是通过displayWith提供的函数。


<!-- Before -->
<mat-slider [displayValue]="myDisplayValue"></mat-slider>

<!-- After -->
<mat-slider [displayWith]="myDisplayWithFn">
  <input matSliderThumb>
</mat-slider>

valueText属性已经被删除,以便直接使用native input的aria valueText或使用displayWith属性指定的函数。


<!-- Before -->
<mat-slider [valueText]="myValueText"></mat-slider>

<!-- After (Option 1) -->
<mat-slider>
  <input [attr.aria-valuetext]="myValueText" matSliderThumb>
</mat-slider>

<!-- After (Option 2) -->
<mat-slider [displayWith]="myDisplayWithFn">
  <input matSliderThumb>
</mat-slider>

slider API也发生了更改,出现了两个新组件:MatSliderThumb和MatSliderRangeThumb。它们提供以下属性:

  • @Input() value: number
  • @Output() valueChange: EventEmitter<number>
  • @Output() dragEnd: EventEmitter<MatSliderDragEvent>
  • @Output() dragStart: EventEmitter<MatSliderDragEvent>
  • percentage: number And the following methods:
  • blur
  • focus

为了适应范围滑块,实现方式已从作为表单控件的<mat-slider>元素更改为包含1-2个<input>元素的滑块,这些input元素用作表单控件。值、关联的事件(输入、更改)和标签(aria标签)现在都存在于<input>元素中。
垂直滑块和反转滑块不再受支持,因为它们不再是“材质设计”等级库的一部分。因此,反转和垂直特性已被删除。

垂直滑块和反转滑块不再受支持,因为它们不再是Material Degsin 规范的一部分。因此,反转和垂直特性已被删除。

5.9. Snack Bar

对于简单的、基于文本的Snack Bar来说,没有什么显著的变化。

对于带有动作按钮的简单Snack Bar,其为使用MDC-based的mat-button,因此您的应用程序需要包含基于MDC按钮的Sass主题mixin。

对于使用自定义结构化内容的Snack Bar(如果您调用MatSnackBar.openFromComponent或MatSnackBar.openFromTemplate),您应该使用以下新指令来annotate您的内容:

matSnackBarLabel标记显示给用户的文本

matSnackBarActions标记包含操作按钮的元素

matSnackBarAction标记各个操作按钮

如果您没有指定这些指令中的任何一个,它将把整个自定义组件/模板视为文本。

那些打开Snack Bar的测试代码现在需要在尝试访问Snack Bar的内容之前调用flush()。在运行迁移工具之前更新您的测试以使用component harnesses应该可以实现无缝转换。

5.10. Table

所有单元格都有16px的左右填充,而不仅仅是最左边和最右边的单元格有24px的填充。

标题单元格具有与数据行相同的颜色和文本大小,而不是具有更多的灰色和更小的文本。

默认情况下,单元格文本不再换行。可以通过对表格单元格应用white-space: normal来启用单元格换行。

行高是52像素,而不是48像素。

单元格的box-sizing是border-box,而不是content-box。这可能会影响自定义宽度样式。

表的最后一行不包括底部边框,因为table应具有底部边框。

MatTableDataSource的paginator属性有一个通用接口,该接口与大多数分页器API匹配,但不是全部。您可能需要显式键入分页器才能访问完整的API,例如:new MatTableDataSource<MyData,MatPaginator>();

Flex tables(<mat-table>)在单元格上显示边框,而不是在行上显示边框。

Flex tables(<mat-table>)使用height而不是line-height来设置行高。

5.11. Tabs

当Tab被选中时, 被选中的Tab标题标签文本颜色与主题颜色匹配

标题标签拉伸以填充header容器的宽度。但是这个行为可以被关闭, 要关闭拉升效果, 可以将<mat-tab-group>的mat-stretch-tabs属性设置为 false。

<mat-tab-nav-bar>需要使用tabPanel属性引用一个<mat-tab-nav-panel>。<mat-tab-nav-panel> 必须包裹连接到导航栏的内容。

<!-- Before -->
<mat-tab-nav-bar>...</mat-tab-nav-bar>

<!-- After -->
<mat-tab-nav-bar [tabPanel]="tabPanel">...</mat-tab-nav-bar>
<mat-tab-nav-panel #tabPanel>...</mat-tab-nav-panel>

5.12. Tooltip

背景颜色是不透明的,而不是略微透明的。这提高了可访问性。

默认字体大小是12px,而不是10px。

行高是normal ,而不是16px。

文本溢出是省略号而不是clip。

有一个新的最小宽度40px。

单行工具提示的文本对齐方式居中。多行工具提示使用左对齐。

6. 后记

本技术博客原创文章位于鹏叔的技术博客空间 - MDC-based Angular Material组件迁移, 要获取最近更新请访问原文. 更多Angular相关文章请访问鹏叔的技术博客空间 - Angular专题

更多技术博客请访问: 鹏叔的技术博客空间

更多Angular Material相关文章请站内搜索Angular 和Material.

7. 参考文档

Migrating to MDC-based Angular Material Components

Angular Schematics 介绍

Angular 15 现已上线:所有新功能、代码实践更改和弃用信息!

Guide to update your Angular application v13.0 -> v15.0 对于 medium applications