如何在 Angular 组件 HTML DOM 中注入 SVG 图标精灵?

Posted

技术标签:

【中文标题】如何在 Angular 组件 HTML DOM 中注入 SVG 图标精灵?【英文标题】:How to inject SVG icon sprites in Angular component HTML DOM? 【发布时间】:2018-10-31 17:26:32 【问题描述】:

我正在构建一个 Angular 应用程序 (Angular 4/5/6),并希望在我的组件模板中使用 SVG 精灵。

问题: 假设我已经生成了我的 SVG 图标精灵 (icons.svg),我怎样才能让 Angular 将我的 SVG 图标精灵注入/导入到我的组件模板中?

有没有一种方法可以将我的 SVG 图标精灵注入/导入到我的组件中,而无需使用任何 3rd 方模块/指令并使用 Angular 本身进行本机操作?

背景/问题:

如this article 中所述,icons.svg 文件将包含定义为<symbol> 的所有 SVG 图标。然后我可以使用<use> 在我的html 中呈现选定的图标,假设icons.svg 被注入到DOM 中。

我使用IcoMoon app 生成了 SVG 精灵,并将icons.svg 保存到我的 Angular 应用程序中。下面是我的示例 Angular 组件 (app.component.ts),我在其中尝试注入/导入 icons.svg 文件并尝试在我的 HTML 中呈现 SVG 图标。但是,Angular 不会渲染我的 SVG 图标。我似乎错误地注入了 SVG 图标精灵文件。

更新:

    我已经知道一个类似的问题,SVG icon system with angular-cli,建议的答案是使用 Node 模块 svg-sprite 使用 CSS 模式生成 SVG 精灵。然而,这不是我要问的。我不是想生成 SVG 精灵。我正在尝试让 Angular 组件了解我的 SVG 精灵 icons.svg,并让它在我使用它们时在 HTML 中呈现它们。 建议了一个解决方案,https://***.com/a/50460361/4988010,从 SVG 精灵生成 CSS 字体。我觉得这不是一个可行的解决方案,而是无法使用 SVG sprite 的“解决方法”。

app.component.ts:StackBlitz 上的实时示例:https://stackblitz.com/edit/angular-bbr2kh?file=src/app/app.component.ts

import  Component  from '@angular/core';
// import `./icons.svg`; // This import method doesn't work


@Component(
  selector: 'my-app',
  template: `

   <!-- This import method doesn't work -->
   <!-- <script src="./icons.svg"></script>  -->

  <p>
  Hello this is a sample Angular 6 component.
  </p>

  <p>Testing to see if SVG icon sprite import works. See below if icons appear. </p>
  <p>Icon (home): <svg class="icon icon-home"><use xlink:href="#icon-home"></use></svg> </p>
  <p>Icon (rocket): <svg class="icon icon-rocket"><use xlink:href="#icon-rocket"></use></svg> </p>
  <p>Icon (wifi): <svg class="icon icon-connection"><use xlink:href="#icon-connection"></use></svg>

  <!-- This import method doesn't work -->
  <!-- <p>Icon (home): <svg class="icon icon-home"><use xlink:href="./icons.svg#icon-home"></use></svg> </p>-->

   </p>
  `,
  styles: [`

  .icon 
  display: inline-block;
  width: 1em;
  height: 1em;
  stroke-width: 0;
  stroke: currentColor;
  fill: currentColor;
  

  `]
)
export class AppComponent 

【问题讨论】:

创建一个 my-icon 组件,该组件引用您的 @input 精灵表。检查这个...Stackblitz 【参考方案1】:

我认为你的根路径是你遇到问题的地方。在您的代码中,您告诉 Angular 查看app,但浏览器将其视为https://your-site.com./icons

如果您将图标文件移动到 /assets 文件夹下,那么它应该已经可用,因为 src\assets 文件夹已经包含在 angular.json 中,"assets" 集合告诉 Angular 去哪里查找。

<svg class="icon">
   <use xlink:href="/assets/icons.svg#icon-rocket"></use> // Notice root path not "./"
</svg>

如果您希望从另一个目录提供文件,您只需在 angular.json 中添加一个路径:

…
"assets": [
   "src/favicon.ico",
   "src/assets",
   "src/your-dir"
],
…

然后在你的代码中

<svg class="icon">
   <use xlink:href="/your-dir/icons.svg#icon-rocket"></use>
</svg>

我不建议将/src/app 添加为资产路径,这基本上会打开您的整个应用程序以提供文件,从而使您的整个目录都可以访问。

我 fork 你的例子并更新了here

【讨论】:

如果我在多个屏幕上使用来自 SVG sprite 的图标,那么 sprite 渲染单个图标将是一个单独的网络调用,不是吗?有没有办法在应用程序启动时将此调用限制为一次? @parthsw 如果您想在加载时加载所有精灵,我会将所有精灵保存在一个文件中 this article 解释得很好,简而言之,您将所有精灵作为符号添加到一个 svg文件,然后像 &lt;svg&gt;&lt;use xlink:href="assets/svgSprites.svg#spriteID"&gt;&lt;/use&gt;&lt;/svg&gt; 一样使用它【参考方案2】:

我使用有角材质图标解决了这个问题。

Angular 材质通过 id 注入 SVG,没有 shadow-dom

先安装角材质:

"@angular/material": "^7.3.7", // change version according to your angular version 

导入您的模块:

MatIconModule

然后在 app.component 或 service 中声明你的 SVG sprite..

    constructor(
        private matIconRegistry: MatIconRegistry,
        private domSanitizer: DomSanitizer)  
matIconRegistry.addSvgIconSet(this.domSanitizer.bypassSecurityTrustResourceUrl(`assets/sprite.svg`));
        

最后,在 Html 中使用角材质图标:

<mat-icon svgIcon="svg-id"></mat-icon> // or use as directive <div svgIcon="svg-id"></div>

Read more here

【讨论】:

很棒的解决方案,它还有一个额外的主要好处是允许从远程 URL 设置 SVG 样式,否则这是不可能的。【参考方案3】:

您可以通过https://icomoon.io/app/#/select将您的svg文件转换为字体,上传您的svg文件并选择它,点击Generate Font然后点击下载按钮, 下载字体文件并选择字体文件夹并将其放在项目中的文件夹中,

将字体文件导入css文件

@font-face 
   font-family: 'icomoon';
   src:  url('fonts/icomoon.eot?31svmk');
   src:  url('fonts/icomoon.eot?31svmk#iefix') format('embedded-opentype'),
   url('fonts/icomoon.ttf?31svmk') format('truetype'),
   url('fonts/icomoon.woff?31svmk') format('woff'),
   url('fonts/icomoon.svg?31svmk#icomoon') format('svg');
  font-weight: normal;
  font-style: normal;

像这样清除你的班级

.icon-home:before 
  content: "\ea77";

.icon-conection:before 
  content: "\ea78";

.icon-rocket:before 
 content: "\ea79";

并在您的 html 中使用它

  <p>Icon (rocket): <i class="icon-rocket"></i> </p>

解决方案是在项目中添加图标,但是如果要在项目中使用svg代码,最好在其中添加加载器。

如果你使用过 webpack:

  npm install svg-inline-loader --save-dev

像这样简单地将配置对象添加到 module.loaders。


    test: /\.svg$/,
    loader: 'svg-inline-loader'

more info

【讨论】:

很高兴知道我可以将我的 SVG 精灵转换为字体,但是,我认为这不是可行的解决方案。为什么我不能直接使用 SVG 精灵本身?为什么我必须绕开主要问题? 我已经遇到了这个问题,我不得不使用这个解决方案,这个方法解决了你的问题。除非你有其他理由在 Angular 中使用 svg 如果您使用WebPack,您可能可以通过添加加载程序来解决您的问题,我在上面的文字中添加了解释。希望对你有帮助 我不明白为什么我们必须使用“CSS/Font”解决方法而不是能够直接使用 SVG。这是Angular中的错误吗? Angular 是否不够灵活,无法支持加载外部文件等基本功能? @AlirezaVarmaghani 在创建生产构建时直接将文件添加到它的根目录(svgs 和字体)。它应该直接导入 CSS 本身。【参考方案4】:

我遇到了一个类似的问题,并使用与给出的一些答案类似的技术解决了这个问题。根据需要使用可选的高度和宽度设置...

<app-icon icon="filter"  ></app-icon>

<app-icon icon="filter"></app-icon>

该组件在icon.component.svg 中只有两个图标(我目前都在使用):

<!-- https://icons.getbootstrap.com/ -->
<div [ngSwitch]="icon">
    <svg *ngSwitchCase="'filter'" xmlns="http://www.w3.org/2000/svg" [attr.width]="width" [attr.height]="height" fill="currentColor" viewBox="0 0 16 16">
        <path d="M1.5 1.5A.5.5 0 0 1 2 1h12a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-.128.334L10 8.692V13.5a.5.5 0 0 1-.342.474l-3 1A.5.5 0 0 1 6 14.5V8.692L1.628 3.834A.5.5 0 0 1 1.5 3.5v-2zm1 .5v1.308l4.372 4.858A.5.5 0 0 1 7 8.5v5.306l2-.666V8.5a.5.5 0 0 1 .128-.334L13.5 3.308V2h-11z"/>
    </svg>
    <svg *ngSwitchCase="'tags'" xmlns="http://www.w3.org/2000/svg" [attr.width]="width" [attr.height]="height" fill="currentColor" viewBox="0 0 16 16">
        <path d="M3 2v4.586l7 7L14.586 9l-7-7H3zM2 2a1 1 0 0 1 1-1h4.586a1 1 0 0 1 .707.293l7 7a1 1 0 0 1 0 1.414l-4.586 4.586a1 1 0 0 1-1.414 0l-7-7A1 1 0 0 1 2 6.586V2z"/>
        <path d="M5.5 5a.5.5 0 1 1 0-1 .5.5 0 0 1 0 1zm0 1a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3zM1 7.086a1 1 0 0 0 .293.707L8.75 15.25l-.043.043a1 1 0 0 1-1.414 0l-7-7A1 1 0 0 1 0 7.586V3a1 1 0 0 1 1-1v5.086z"/>
    </svg>
</div>

icon.component.ts:

import  Component, Input, OnInit  from '@angular/core';

@Component(
    selector: 'app-icon',
    templateUrl: './icon.component.svg',
    styleUrls: []
)
export class IconComponent implements OnInit
    @Input() icon: any
    @Input() width?: any
    @Input() height?: any

    ngOnInit(): void 
        if (typeof this.width != 'string') 
            this.width = '20'
        
        if (typeof this.height != 'string') 
            this.height = '20'
        
    

无需包含整个图标库集,在应用程序中重复使用相当简单。

【讨论】:

【参考方案5】:

今天我和你在同一条船上,设计师反对 svg 字体。我也不想将图标 def 从 node_modules 中移出,因为它会随着时间而改变。我想出了一个解决方案。

你需要先创建一个角图标组件,然后做这样的事情:

import  Component, OnInit, Input  from '@angular/core';
declare const require;
@Component(
  selector: 'my-icon',
  templateUrl: './my-icon.component.html',
  styleUrls: ['./my-icon.component.scss']
)
export class IconComponent implements OnInit 

  constructor()  

  @Input() icon: string = "";
  @Input() x: string = "0";
  @Input() y: string = "0";
  @Input() fillColor: string = "black";
  @Input() strokeColor: string = "black";
  @Input() height: string = "";
  @Input() width: string = "";
  private baseUrl: string = require("../../../../../node_modules/path/to/defs.svg") + "#beginning-of-iconset-";

  svgUrl: string = "";

  ngOnInit() 
  

  ngAfterViewInit() 
    setTimeout(()=>
      this.svgUrl = this.baseUrl + this.icon;
    ,0);

  


然后,在 html 中:

<svg [attr.height]="height" [attr.width]="width" xmlns="http://www.w3.org/2000/svg">
  <use [attr.href]="svgUrl" [attr.x]="x" [attr.y]="y" [attr.fill]="fillColor" [attr.stroke]="strokeColor" />
</svg>

我仍在努力缩放解决方案,因为宽度和高度属性无法按预期工作。你也可以扩展输入,我知道我会这样做。

希望对您有所帮助。

【讨论】:

以上是关于如何在 Angular 组件 HTML DOM 中注入 SVG 图标精灵?的主要内容,如果未能解决你的问题,请参考以下文章

在 Angular 中,你将如何在 dom 中移动组件?

如何使用 Angular 4 从 Parent 获取子 DOM 元素引用

如何在angular2和ionic2中同时保留组件的DOM元素和模板字符串

Angular 2 将组件动态附加到 DOM 或模板

如何将 DOM 附加到 Angular 2 组件并仍然获得封装样式

Angular 2:删除组件中的包装 DOM 元素