如何在 Angular 5 中从文件导入 SVG 到组件?

41

我找到的所有关于在AngularCli组件中添加SVG的教程都建议将其插入HTML模板中,类似这样:

<div>
  <svg viewBox="0 0 250 250">
    <svg:g class="group">
       <svg:polygon class="shield" points="125,30 125,30 125,30 31.9,63.2 46.1,186.3 125,230 125,230 125,230 203.9,186.3 218.1,63.2" />
       <svg:path class="a" d="M125,52.1L66.8,182.6h0h21.7h0l11.7-29.2h49.4l11.7,29.2h0h21.7h0L125,52.1L125,52.1L125,52.1L125,52.1
      L125,52.1z M142,135.4H108l17-40.9L142,135.4z"/>
    </svg:g>
  </svg>
</div>

但我希望保持模板的清晰,只在其中插入少量标签,并使用指向分离的svg文件的url,类似于这样:

<svg class="star">
        <use xlink:href="../../../assets/images/svg/star.svg"
               x="0"
               y="0" />
</svg>

如何在组件中使用分离的SVG文件?

9个回答

43

如果您有 logo.svg

  1. 将其放在您的src/assets文件夹中
  2. angular.json配置中包含文件夹:"assets": ["src/assets"]
  3. 从模板中引用它:<img src="assets/svg/logo.svg">

如果它是一个库怎么办?当我尝试这样做并尝试运行在同一工作区中使用该库的应用程序时,会出现以下错误:An unhandled exception occurred: The projects/my-lib/src/assets asset path must start with the project source root。但是,如果我从src开始,它不可能知道我是指库还是应用程序。以与应用程序的资产文件夹相同的方式从projects开始引用可以正常工作。 - Guilherme Taffarel Bergamin

20
将您的SVG文件放在src/assets文件夹中,并将svg文件夹添加到您的angular.json文件中。
"assets": [ "src/assets/svg/*" ]

这样,您就可以根据需要在组件中包含文件。


谢谢!与其一个一个地添加SVG文件,不如添加SVG文件夹“assets”:["src/assets/svg/*"]。 - mr_blond
3
如果它是一个库怎么办?当我尝试这样做并尝试运行在同一工作区中使用该库的应用程序时,会出现以下错误:An unhandled exception occurred: The projects/my-lib/src/assets asset path must start with the project source root。但是,如果我从src开始,它不可能知道我是指库还是应用程序。以与应用程序的资产文件夹相同的方式从projects开始引用可以正常工作。 - Guilherme Taffarel Bergamin

17

TL;DR, 使用 HttpClient 获取文件,然后使用 bypassSecurityTrustHtml[innerHTML] 渲染它。

这可能有点晚了,但是我们找到了解决方案的方法。我们尝试查找 Angular Material 是如何处理其图标的,并惊讶地发现它真的很简单。他们只是使用 HttpClient 来获取文件!这已经在我们的脑海中了,但我们一直忽略它,因为我们认为可能有更好的解决方案。

所以在搜索了几分钟后,我们偶然发现了这个链接: https://github.com/angular/components/blob/653457eaf48faab99227f37bc2fe104d9f308787/src/material/icon/icon-registry.ts#L621

所以基本上,如果您的 SVG 文件在资产文件夹 (/assets/images/svg/star.svg) 中的任何位置,您需要做的就是使用 HttpClient.get 来获取它,然后使用 DomSanitizer 来绕过安全性并信任给定的值是安全的 HTML,然后才能将其呈现到您的组件中。

最后,这是我们的组件的样子:

import { Component, OnChanges, SecurityContext } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { DomSanitizer } from '@angular/platform-browser';

@Component({
  selector: 'app-svg-icon',
  template: `<span [innerHTML]="svgIcon"></span>`,
  styleUrls: ['./svg-icon.component.scss'],
})
export class SvgIconComponent implements OnChanges {

  @Input()
  public name?: string;

  public svgIcon: any;

  constructor(
    private httpClient: HttpClient,
    private sanitizer: DomSanitizer,
    ) {
  }

  public ngOnChanges(): void {
    if (!this.name) {
      this.svgIcon = '';
      return;
    }
    this.httpClient
      .get(`assets/images/svg/${this.name}.svg`, { responseType: 'text' })
      .subscribe(value => {
        this.svgIcon = this.sanitizer.bypassSecurityTrustHtml(value);
      });
  }

}

现在,您只需将该组件导入到应用程序的任何位置:

<app-svg-icon name="star"></app-svg-icon>

1
@jsnewbie,这对于具有内部内联图标组件的应用程序非常有效。但是,如果我想将图标作为库的一部分进行重写,然后使用该库调用图标,类似于mat-icon,我该怎么做?我还想在我的库中提供图标,而不仅仅是使用应用程序中的图标组件。另外,我应该如何控制图标的大小?像OP提到的<svg>标签一样,我可以直接设置heightwidth并将它们作为参数传递给我的组件。 - Guilherme Taffarel Bergamin
1
@GuilhermeTaffarelBergamin 我认为你可以按照这里的回答,将你的库的资源包含在构建中:https://dev59.com/blMH5IYBdhLWcg3w4E7k - jsnewbie
2
@mhombach 一般来说,mat-icon 库就是这样工作的,而且这个功能是有原因创建的。你只需要知道何时以及如何使用它即可。干杯! - jsnewbie
@mhombach 抱歉,但您未能回答我的问题。您似乎无法在这里论证您的观点。我相信在这种特定情况下,没有任何方法可以使用XSS来利用未经过净化的图像。而且有一个赞成的观点,那就是性能。我确实同意您的观点,在一般情况下,净化是必须的,您能否提出任何情况,在此用例中不这样做会有问题? - maybesomename
@mhombach 我不认为Stackoverflow支持像你这样基于观点的争论。既然你无法支持自己的立场(除了转移话题),那么它就只是一种观点。如果人们滥用这个答案,那就是他们的责任。如果你声称这个答案是危险的,但实际上并非如此,并且你无法提出论据,那就是你的责任。而且这是误导性的。在这里没有必要个人攻击。 - maybesomename
显示剩余13条评论

8

实现此功能的一种方法是为您的SVG文件设置id属性,并将SVG文件放置在asset文件夹中。 然后在mat-icon中使用该id,如下所示:

<mat-icon svgIcon="my-star-icon"></mat-icon>

这是一种更好的方法;这样做可以避免在UI HTML代码中处理SVG标签。此外,它支持Google图标。
如果您正在使用Angular Material,则可以使用此方法。
编辑: 为了使其起作用,您需要在组件中使用IconRegistry注册图标。
  constructor(iconRegistry: MatIconRegistry, sanitizer: DomSanitizer) {
    iconRegistry.addSvgIcon(
        'my-star-icon',
        sanitizer.bypassSecurityTrustResourceUrl('assets/icons/my-star-icon.svg'));
  }

点击此处查看文档,点击此处查看示例。


5
需要使用 Angular Material 进行额外的设置。你能否更新你的回答并添加这些内容?如果不能,我可以继续编辑你的回答。 - Ojas Deshpande
@OjasDeshpande,你可以编辑我的答案。我没有使用过更新版本。感谢你指出来。 - Arya11

2

然而,有一种更优雅的方法,它暗示着svg文件具有与文件名相同的ID。

SVG组件:

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

@Component({
  selector: 'app-svg-icon',
  template: '
    <svg attr.width="{{width}}px" attr.height="{{height}}px" attr.fill="{{fill}}" attr.class="{{class}}">
      <use attr.xlink:href="assets/icons/{{icon}}.svg#{{icon}}"></use>
    </svg>
  ',
})
export class SvgIconComponent implements OnInit {
  @Input() icon!: string;
  @Input() width?: number;
  @Input() height?: number;
  @Input() size?: number = 24;
  @Input() fill?: string;
  @Input() class?: string;

  ngOnInit(): void {
    if (!this.width || !this.height) {
      this.width = this.size;
      this.height = this.size;
    }
  }
}

假设您有一个svg图标在文件夹中:/assets/icons/person.svg

该svg本身包含以下代码(为了方便更改svg的大小和颜色,它不应包含高度、宽度和填充属性):

<svg id="person" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
    <path d="M12 5.9c1.16 0 2.1.94 2.1 2.1s-.94 2.1-2.1 2.1S9.9 9.16 9.9 8s.94-2.1 2.1-2.1m0 9c2.97 0 6.1 1.46 6.1 2.1v1.1H5.9V17c0-.64 3.13-2.1 6.1-2.1M12 4C9.79 4 8 5.79 8 8s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4zm0 9c-2.67 0-8 1.34-8 4v3h16v-3c0-2.66-5.33-4-8-4z" />
</svg>

现在,您可以在任何组件中使用您的图标:

<app-svg-icon [icon]="'person'" fill="red" [size]="48"></app-svg-icon>


我喜欢这个。我们一直在我们的应用程序中使用它。但现在我需要它成为库的一部分,而且库将必须提供图标。我只能使用应用程序“资产”文件夹中存在的图标,而不能使用库中的图标。 - Guilherme Taffarel Bergamin

2

Angular - 创建一个组件,添加很多内容等,然后获取您的 SVG。

React - 只需导入您的 SVG 并继续工作。

(是的,它将被添加为标签。您可以使用 CSS 填充它,随意更改它)

比较 React 和 Angular 导入 SVG


你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心中找到有关如何编写良好答案的更多信息。 - Community
根据这次讨论,Angular的方法允许在服务器端生成SVG,并将其包含在DOM中。这与React Import和一些其他解决方案所做的不同。React Import和将文件放入资源文件夹会将SVG放入捆绑包中,这可能是某些人想要的,但对于图表等内容,HTTP方法似乎是最好的综合答案。 - TJ Bandrowsky

2

我一直在尝试做这件事,但svg图像怎么也显示不出来…… 经过多次搜索后,我发现问题可能不止出现在我身上。如果你从互联网上复制粘贴路径,那么你可能忘记了包括以下内容:

xmlns="http://www.w3.org/2000/svg"

<svg xmlns="http://www.w3.org/2000/svg" height="100" width="100">
<path d="I love copying and pasting paths"/>
</svg>

那么您可以继续进行您的操作。
 <img src="assets/img/logo.svg" />

如果这种方法不起作用,而你想使用图片,则将图片放在assets/img/copypasta.png中。


0

import { DomSanitizer } from '@angular/platform-browser';


  constructor( private sanitizer:DomSanitizer) { }
steps:any
  ngOnInit() {
    this.steps=this.sanitizer.bypassSecurityTrustHtml(this.step)
  }
  
  step:any=[
    `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1920" height="1080" viewBox="0 0 1920 1080">
  <defs>
    <clipPath id="clip-Web_1920_1">
      <rect width="1920" height="1080"/>
    </clipPath>
  </defs>
  <g id="Web_1920_1" data-name="Web 1920 – 1" clip-path="url(#clip-Web_1920_1)">
    <rect width="1920" height="1080" fill="#fff"/>
    <line id="Line_1" data-name="Line 1" y1="2" x2="436" transform="translate(0.5 457.5)" fill="none" stroke="#707070" stroke-width="1"/>
    <g id="Rectangle_1" data-name="Rectangle 1" transform="translate(437 395)" fill="#df5151" stroke="#707070" stroke-width="1">
      <rect width="212" height="121" stroke="none"/>
      <rect x="0.5" y="0.5" width="211" height="120" fill="none"/>
    </g>
    <g id="Rectangle_2" data-name="Rectangle 2" transform="translate(894 657)" fill="#d95ed5" stroke="#707070" stroke-width="1">
      <rect width="255" height="96" stroke="none"/>
      <rect x="0.5" y="0.5" width="254" height="95" fill="none"/>
    </g>
    <line id="Line_2" data-name="Line 2" y2="189" transform="translate(543.5 516.5)" fill="none" stroke="#707070" stroke-width="1"/>
    <line id="Line_3" data-name="Line 3" x2="351" transform="translate(543.5 705.5)" fill="none" stroke="#707070" stroke-width="1"/>
    <g id="Ellipse_1" data-name="Ellipse 1" transform="translate(455 633)" fill="#1acf38" stroke="#707070" stroke-width="1">
      <ellipse cx="88.5" cy="72.5" rx="88.5" ry="72.5" stroke="none"/>
      <ellipse cx="88.5" cy="72.5" rx="88" ry="72" fill="none"/>
    </g>
    <text id="Login" transform="translate(510 455)" fill="#f8f1f1" font-size="20" font-family="SegoeUI, Segoe UI"><tspan x="0" y="0">Login</tspan></text>
    <text id="True" transform="translate(520 716)" fill="#f6f1f1" font-size="25" font-family="SegoeUI, Segoe UI"><tspan x="0" y="0">True</tspan></text>
    <text id="Logged_In" data-name="Logged In" transform="translate(977 701)" fill="#fcf8f8" font-size="20" font-family="SegoeUI, Segoe UI"><tspan x="0" y="0">Logged In</tspan></text>
  </g>
</svg>
`
    ]
<div [innerHTML]="steps"></div>


请不要只发布代码答案,而是添加一些文本说明您的方法如何工作以及它与其他给出答案的不同之处。您还可以查看我们的“如何撰写好答案”条目。 - ahuemmer

0

使用SVG作为模板

您可以直接使用SVG而不是HTML作为组件模板 - 更多细节在这里

工作示例在这里

@Component({
  selector: 'app-my-svg',
  templateUrl: './my-svg.component.svg',
  styleUrls: ['./my-svg.component.css']
})
export class MySvgComponent {
...

在这个SVG模板中(它在单独的文件中!),你可以使用Angular方式的样式和变量


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接