tltr:我可以从 Angular 访问资产目录吗?
我在一个文件夹
src/assets/icons
中有一堆 SVG,我与 mat-icon
一起使用。唯一的缺点是,当我添加新图标时,我必须添加文件,并且必须将文件名添加到一个数组中,我用该数组循环将其添加到matIconRegistry
。有没有办法简单地读取目录中的所有文件?
这是我当前的解决方案:
@NgModule()
export class CustomIconModule {
constructor(
private matIconRegistry: MatIconRegistry,
private domSanitizer: DomSanitizer
) {
const icons = [
'icon1', 'icon2',
];
for (const icon of icons) {
this.matIconRegistry.addSvgIconInNamespace(
'my-namespace',
icon,
domSanitizer.bypassSecurityTrustResourceUrl(`./assets/icons/${icon}.svg`)
);
}
}
}
更新:我编写了一个 bash 脚本,它循环遍历文件,生成一个包含所有文件名数组的 .ts 文件,并执行
git add
。我在预提交 git hook 中运行它。不太理想,但目前还可以。不过,我很想找到一个纯 JS 解决方案。
您可以在应用程序的构建阶段执行节点脚本。
// scripts/icons-lookup.js
const fs = require('fs');
const glob = require('glob');
const ICONS_DIRECTORY_PATH_PATTERN = 'src/assets/icons/**/*.svg';
const ICONS_FILE_PATH_PATTERN = /^src[\\/]assets[\\/]icons[\\/](?<iconFilePath>.+)\.svg$/;
const OS_SEPARATOR = require('path').sep;
const result = {};
glob.sync(ICONS_DIRECTORY_PATH_PATTERN).forEach((path) => {
const match = ICONS_FILE_PATH_PATTERN.exec(path);
const fragments = match[1].split(OS_SEPARATOR);
const key = fragments.join('-').replace(' ', '-');
const value = `${fragments.join('/')}.svg`;
result[key] = value;
});
fs.writeFileSync('./src/assets/icons-lookup.json', JSON.stringify(result));
然后,定义一个
icons:lookup
命令,该命令在任何 ng build
/ ng serve
: 之前调用
// package.json
{
"name": "my-application",
"scripts": {
"icons:lookup": "node ./scripts/icons-lookup.js", // this calls the above node script
"before:run": "npm run icons:lookup", // intermediate command
...
"build": "npm run before:run && ng build --configuration local",
"build:dev": "npm run before:run && ng build --configuration dev",
...
"serve": "npm run before:run && ng serve --configuration local",
"serve:dev": "npm run before:run && ng serve --configuration dev",
...
},
注意:我建议使用中间
before:run
命令,您可以在其中添加大量其他脚本然后,当您运行应用程序时,它会生成一个 icons-lookup.json 文件(将其添加到您的 .gitignore):
{"icon1":"icon1.svg","icon2":"icon2.svg",...}
最后,您可以在模块(或服务)中利用此文件。
您可以通过
require()
同步获取,也可以通过 this.http.get
调用异步获取。
// src/app/modules/some-module/services/icon-registry/icon-registry.service.ts
import { inject, Injectable } from '@angular/core';
import { MatIconRegistry } from '@angular/material/icon';
import { DomSanitizer } from '@angular/platform-browser';
const fileMap: Record<string, string> = require('../../../../../assets/icons-lookup.json');
@Injectable({ providedIn: 'root' })
export class IconRegistryService {
private readonly matIconRegistry = inject(MatIconRegistry);
private readonly sanitizer = inject(DomSanitizer);
private initialized = false;
initialize(): void {
if (!this.initialized) {
Object.entries(fileMap).forEach(([iconName, fileName]) => {
const filePath = `assets/icons/${fileName}`;
const url = this.sanitizer.bypassSecurityTrustResourceUrl(filePath);
this.matIconRegistry.addSvgIcon(iconName, url);
});
this.initialized = true;
}
}
}
// main.ts
const appConfig: ApplicationConfig = {
providers: [
...,
{
provide: APP_INITIALIZER,
useFactory: (service: IconRegistryService) => () => {
service.initialize();
},
deps: [IconRegistryService],
multi: true,
},
],
};
bootstrapApplication(AppRootComponent, appConfig).catch((err) => {
console.error(err);
});