在Angular中创建一个与Angular Material主题兼容的库
-
- Angularを使う
-
- Angular Materialを使う
-
- Angular Materialにカスタムテーマを設定する
- アプリケーションと同一環境で動作するライブラリでAngular Materialのテーマを利用
尽管有很多关于这个主题的文章,但我在创建一个库并在该库中使用Angular Material主题的过程中(无论是用日语还是英语)都找不到相关信息,所以我决定总结一下。我正在尝试使用Angular v6版本。
我会假设您已经安装了Angular的CLI,并且已经完成了最初的教程。在这个前提下,我将介绍一些技巧,使用TypeScript来开发某种框架的库,创建与主题对应的UI框架,并积极使用CSS in JS,以便将UI框架与外部库的主题进行匹配。我认为这些技巧在解决一般需求时也会很有参考价值。
创建一个图书馆
在当前以转译为前提的JavaScript中,最困难的是创建适用于转译前提环境的库。需要同时考虑库的创建者和使用者的构建环境。幸运的是,由于Java文化的影响力深厚,Angular作为一个代码生成器,其工作流程和命令行工具已经包含了相关功能。
无法单独创建库项目,需要首先创建一个使用该库的主应用程序,然后在主应用程序中构建库。库的名称是awesome-lib,前缀是al。最终的结构将是像这样的。
首先,我们将创建一个作为开发库基础的应用程序。稍后会提到,我们将使用SCSS作为样式表。嗯,即使忘记了,也不用担心,只需要稍后修改angular.json文件或组件中的样式表文件名,并将生成的各种css文件的扩展名更改为.scss,就可以重新进行了,不需要重新做。
$ ng new awesome-lib-sample --style=scss
接下来,在这个文件夹中创建一个库。如果忘记了前缀,它会变成”lib-“,听起来很土气,请务必记住前缀。
$ ng generate library awesome-lib --style=scss --prefix=al
好的。现在,库文件夹已经在”projects/awesome-lib”下创建好了。里面包含了库的package.json和其他一些内容,所以你可以添加额外需要的包并进行其他一些操作。
在母舰应用的根目录中完成诸如添加组件之类的操作。
$ ng generate component new-component --project=awesome-lib
可以在Root的母船应用文件夹下执行构建。按照以下方式进行构建,构建后的文件、project.json等将会在dist/awesome-lib文件夹及其子文件夹中生成。
$ ng build awesome-lib
这个dist/awesome-lib文件夹中包含了要在npm包中发布的内容。只需要在这个文件夹中使用npm publish命令,就可以快速地在npm上发布。
此外,使用方的母舰应用程序中
import { AwesomeLibModule } import "awesome-lib";
只需一个选项:
我们可以像安装已经发布的软件包一样,可以通过import命令导入。这是因为当我们使用ng generate library命令时,ng命令会自动在tsconfig.json文件中添加库的别名行。这样就可以在不实际部署的情况下测试生成的代码了。但是需要注意的是,在构建库的步骤中会有一个额外的步骤。
{
"compilerOptions": {
"paths": {
"awesome-lib": [
"dist/awesome-lib"
],
"awesome-lib/*": [
"dist/awesome-lib/*"
]
}
}
}
若将整体构成画成图,就是这个样子。
在浏览互联网时,可能会遇到一些旧的信息(例如创建文件夹或package.json之类的步骤),但最新的v6时代似乎是以下流程。
使用自己创建的库来利用Angular Material的主题。
如果只是简单地使用 Angular Material,从库中导入模块并按照 Angular Material 的教程操作,就能使用 mat- 前缀的各种功能,然后只需将其作为标签或指令进行使用。
然而,使用自定义库来使用Angular Material的主题颜色等需要花费一些时间和精力。虽然Angular Material中有官方说明,但解释不够详细,所以我会提供补充说明。
- Theming your custom components
如果要概括这个页面的内容,就是定义SCSS的mixin,并在根应用程序中设置Angular Material的主题设置时,以相同的方式为自己的组件设置主题。
Angular中SCSS的构建生命周期
当你创建Angular组件时,其中会设置样式表和HTML。.vue文件的单文件组件将各个元素拆分为不同的文件。你也可以将它们内嵌为字符串。
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'al-my-input',
templateUrl: './my-input.component.html',
styleUrls: ['./my-input.component.scss']
})
export class MyInputComponent implements OnInit {
constructor() {}
ngOnInit() {}
}
在使用库的情况下,通过ng build命令进行构建,.ts文件会被构建并转换为一个JavaScript文件。然而,麻烦之处在于在这里编写的SCSS文件将被转换为CSS in JS,并合并到JavaScript文件中。
换句话说,在这个时机上,像是动态的SCSS代码已经被“执行”了,所以在应用程序加载后设置主题是不可行的。
当然,CSS in JS作为一种方式具有优势,就像WebComponents一样,它可以封装CSS的影响范围,使得我们无需考虑整个应用的CSS模块化设计,只需轻松地编写组件专用的CSS。
Angular Material目前有使用两种类型的SCSS文件来定义组件(有些不相关的内容已经省略)。
/button
_button-theme.scss
button-module.ts
button.scss
button.ts
所以,button.scss是设置组件装饰器的地方。它包含了与主题无关的固定布局信息,例如宽度、z-index、圆角设置等等。另外还有一个scss文件_button-theme.scss,它包含了Angular Material官方指南中介绍的主题设置mixin。
这个包含mixin的代码,会从src/lib/core/theming/_all-theme.scss中被引入,并且会被单独编译到不同于库的.js文件中,在发布文件夹的顶部以_theming.scss的形式存放。
这是官方Angular Material主题设置示例中引入的theming的实体部分。我们将动态设置的颜色信息单独提取到另一个文件中,供应用程序调用。
@import '~@angular/material/theming'; // これ
@include mat-core();
$candy-app-primary: mat-palette($mat-indigo);
$candy-app-accent: mat-palette($mat-pink, A200, A100, A400);
$candy-app-warn: mat-palette($mat-red);
$candy-app-theme: mat-light-theme($candy-app-primary, $candy-app-accent, $candy-app-warn);
@include angular-material-theme($candy-app-theme);
换句话说,我们可以使用两个不同的SCSS文件来区分主题和布局。我们的目标是将库编译成JavaScript,并且将布局用的SCSS转换为JSS in JS的同时,让应用程序能够使用主题设置的SCSS。
在我的图书馆里也做同样的事情。
怎样才能创建一个_theming.scss文件呢?在每个库中都包含有SCSS。那么我们可以使用libsass进行构建吗?答案是否定的。调用libsass会进行函数展开操作,并最终生成普通的CSS,这不会生成可供应用程序使用的SCSS文件。
如果从头开始创建一个设置所有组件的单个SCSS文件,那将会变得非常复杂,尤其是当库中的模块增加时。至少,我们希望将动态主题的SCSS文件按组件进行分割。
我們還有一個能夠正好使用的工具,名為scss-bundle,它只處理@import,而不是完整地編譯SCSS,因此可以從許多SCSS文件中創建一個分發用的單一SCSS文件。
我们将在每个组件中创建一个mixin,使用Angular Material提供的$theme变量和mat-color函数来定义CSS。
@import '~@angular/material/theming';
@mixin my-inut-theme($theme, $guide-colors) {
$primary: map-get($theme, primary);
$accent: map-get($theme, accent);
.al-input {
background-color: mat-color($primary, 100);
}
}
在项目的根目录中,我们将导入每个组件的主题用SCSS,并创建一个包含它们的mixin的主控文件。这将成为应用程序调用的入口点。
@import './src/lib/my-input/theme';
@mixin awesome-lib-theme($theme) {
@include my-input-theme($theme, $guide-colors);
}
我也会写一个用于scss-bundle的配置文件。这样_theming.scss文件就会在分发文件夹的根目录中生成。重要的是,Angular Material提供的scss文件不会被包含在捆绑文件中。
{
"entry": "projects/awesome-lib/_theming.scss",
"dest": "./dist/awesome-lib/_theming.scss",
"ignoredImports": ["~@angular/.*"]
}
最后,我在母舰示例应用程序的package.json中进行了一些更改,使得可以一起构建库和SCSS。我这次使用了scripts命令,但你也可以尝试使用npm-run-all等其他工具。
"scripts": {
"build-lib": "ng build awesome-lib",
"postbuild-lib": "scss-bundle -c projects/awesome-lib/scss-bundle.config.json"
},
最后,我们将在母舰的样式文件styles.scss中导入构建好的文件,并调用mixin。
@import '~@angular/material/theming';
@import '../dist/awesome-lib/theming'; // ←追加
// 省略
@include angular-material-theme($candy-app-theme);
@include awesome-lib-theme($candy-app-theme); // ←追加
现在,即使是自制的库,也可以与Angular Material的主题进行联动使用了。
总结
我以一种让任何人都能使用的方式介绍了在Angular Material中使用的技巧。
Angular Material自身以外にも便利なツールであるAngular CLIなどを活用しているような印象を受けます。ディレクトリ構成からビルド方法まで、すべてが現代的な手法とは異なるものとなっています。例えば、gulpを使用したり、bazelを利用するといった手法があります。そのため、解読には少し時間がかかるかもしれませんが、大規模なUI部品のコレクションを作りたい方や、Angular Materialと連携させてテーマに対応した追加コンポーネントのライブラリを作りたい方にとって、参考になると思います。私も時間を見つけて、Cheetah-GridのAngular版を作ろうと思っています。