Angular 6:无需库即可实现动态主题
主题化的概念由来已久。让用户能够选择产品的外观和风格具有巨大的价值——它能创造更本地化的体验,并减少开发人员的维护时间。
我们如何在Angular应用程序中创建类似的功能?
为什么单靠 Sass 是行不通的
虽然 Sass 变量可以用来创建预设的主题体验,但最大的缺点是它无法被 JavaScript 操作。我们需要用 JavaScript 来动态地改变变量的值!
为什么单靠材料是行不通的
自从 Angular Material 发布以来,开发者们纷纷涌向这个库,利用其可重用的组件(更不用说其内置的辅助功能了)。
Material Design 自带主题功能,但由于以下两个原因,该功能可能无法正常工作:
-
Material 默认自带一套针对无障碍访问优化的调色板。如果需要生成更多颜色,则需要将其传递给 Material 的 mat-palette mixin,或者使用第三方工具创建一个新的主题文件。这样做会引入外部依赖项,并限制在不修改代码的情况下切换主题的能力。
-
虽然 Material 是个不错的选择,但并非所有人都想使用它!许多开发者不希望为了使用组件而导入整个库,而是选择创建自己的组件。
解决方案?Sass + CSS 变量!
如果您从未用过原生 CSS 自定义属性(我称之为变量),这里有一篇很棒的文章(链接在此)可以帮助您入门。这种方法之所以有效,是因为 CSS 变量可以被 JavaScript 操作!通过这种方式,您可以使用表单将 CSS 变量传递给 Sass 映射,从而在整个应用程序中使用该映射。
让我们看看!
此实现方式:
- 未使用任何外部库
- 允许多个组件通过表单动态更改样式。
- 将表单保存为可保存到数据库或本地存储的对象。
- 能够将外部对象加载为预加载或预设样式
演示链接:https://native-theming-form-medium.stackblitz.io/
Stackblitz 链接:https://stackblitz.com/edit/native-theming-form-medium
魔法
该方法的核心原理是将 Sass 映射和 CSS 变量结合起来。
在 theme.scss 文件中,默认值被设置并传递给一个 Sass 映射。
theme.scss
// default colors
.theme-wrapper {
--cardColor: #CCC;
--cardBackground: #FFF;
--buttonColor: #FFF;
--buttonBackground: #FFF;
--navColor: #FFF;
--navBackground: #FFF;
--footerColor: #FFF;
--footerBackground: #FFF;
--footerAlignment: left;
}
// pass variables into a sass map
$variables: (
--cardColor: var(--cardColor),
--cardBackground: var(--cardBackground),
--buttonColor: var(--buttonColor),
--buttonBackground: var(--buttonBackground),
--navColor: var(--navColor),
--navBackground: var(--navBackground),
--footerColor: var(--footerColor),
--footerBackground: var(--footerBackground),
--footerAlignment: var(--footerAlignment)
);
创建了一个函数,用于从全局 Sass 映射中返回原生 CSS 变量。
function.scss
@function var($variable) {
@return map-get($variables, $variable);
}
现在,这些组件可以读取这两个文件,以存储一个在表单重新提交时会发生变化的动态变量。
card.component.scss
@import '../../theme';
@import '../../functions';
.card {
background-color: var(--cardBackground);
color: var(--cardColor);
}
卡片的背景颜色现在是 #FFFFFF,文字颜色是 #CCCCCC
但是我们如何改变这些价值观呢?
通过主题选择器组件!
在我们的 theme-picker.component.html 文件中,我们使用带有 ngModel 的模板表单来创建一个具有唯一键(样式)和值(输入)的对象。然后,该对象被传递给 TypeScript 文件,该文件会动态地覆盖该变量。
主题选择器组件.ts
// searching the entire page for css variables
private themeWrapper = document.querySelector('body');
onSubmit(form) {
this.globalOverride(form.value);
}
globalOverride(stylesheet) {
if (stylesheet.globalNavColor) {
this.themeWrapper.style.setProperty('--navColor', stylesheet.globalNavColor);
}
...
if (stylesheet.globalButtonColor) {
this.themeWrapper.style.setProperty('--buttonColor', stylesheet.globalButtonColor);
}
}
globalOverride 函数会检查该特定变量是否存在值,然后将每个 CSS 变量替换为新输入的值。
Violá!
这段代码可以进一步优化以适应规模(使用预设样式对象,在提交时保存/发布样式),所以请随意尝试!
文章来源:https://dev.to/adamaso/angular-6-dynamic-themes-without-a-library-2e9c

