How do I import svg from file to a component in angular 5?
Asked Answered
W

9

42

All tutorials with adding svg to a component in AngularCli that I found recommend to insert it in html template, something like this:

<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>

But I wish to keep templates clear and instert only few tags in it with url to separated svg file, somwehow like this:

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

Ho do I use separated svg files in components?

Waller answered 30/10, 2018 at 14:41 Comment(0)
D
19

Include your SVG files in src/assets folder and add the svg folder in your angular.json file.

"assets": [ "src/assets/svg/*" ]

This way you can include the file in your components as you wish.

Drida answered 30/10, 2018 at 14:56 Comment(2)
Thanks! One rather add svg folder "assets": [ "src/assets/svg/*" ] than each svg file.Waller
What if it is a lib? When I tried that and tried running the app that's in the same workspace and is using the lib, it gets the following error: An unhandled exception occurred: The projects/my-lib/src/assets asset path must start with the project source root. But if I start from src, there is no way it's going to know if I mean the lib or the app. Referring to the app's assets folder in the same way starting from projects works finePontiac
O
45

If you have logo.svg:

  1. Put it inside your src/assets folder
  2. Include folder in the angular.json config: "assets": [ "src/assets" ]
  3. Refer to it from template: <img src="assets/svg/logo.svg">
Opportunity answered 27/11, 2019 at 20:32 Comment(1)
What if it is a lib? When I tried that and tried running the app that's in the same workspace and is using the lib, it gets the following error: An unhandled exception occurred: The projects/my-lib/src/assets asset path must start with the project source root. But if I start from src, there is no way it's going to know if I mean the lib or the app. Referring to the app's assets folder in the same way starting from projects works finePontiac
T
21

TL;DR, use HttpClient to fetch for the file, then use bypassSecurityTrustHtml to render it with [innerHTML].

This may be a bit late for an answer, but here's how we found the solution. we tried looking up into how angular material does it for their icons, and boy were we surprised to how simple it really is. They were just fetching the file using HttpClient! It was already at the back of our minds but we kept ignoring it cause we thought maybe there was a better solution.

So after a few minutes of searching, we stumbled upon this: https://github.com/angular/components/blob/653457eaf48faab99227f37bc2fe104d9f308787/src/material/icon/icon-registry.ts#L621

So basically, if you have your SVG file somewhere in your assets folder (/assets/images/svg/star.svg), all you have to do is to fetch it using HttpClient.get and use the DomSanitizer to bypass security and trust the given value to be safe HTML before you can render it to your component.

And finally, here's how our component looks like:

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);
      });
  }

}

Now you can just import the component anywhere in your app as

<app-svg-icon name="star"></app-svg-icon>
Thallus answered 24/4, 2022 at 13:27 Comment(18)
Absolutely the underestimated answer should be known by every angular developer ! Thank you a lot ❤ @Thallus ❤!Emanuel
@jsnewbie, this works great for an app which has an internal inline icon component. But how could I rewrite this to have the icons as part of a lib and then use the lib to call for the icons a la mat-icon? I want to provide icons in my lib as well not only a component to use icons that are part of the app. Also, how am I supposed to control icon size? With the <svg> tag like OP mentioned, I could set height and width directly and pass them as parameters to my component.Pontiac
"to bypass security and trust the given value" that's the worst and most risky advice you can give to a worldwide comunity on stackoverflow.Johnie
@GuilhermeTaffarelBergamin i think you can just include the assets of your lib into the build as answered here: #57741552Thallus
@Johnie that's generally how the mat-icon library works, and that feature was created for a reason. you just have to know when and how to use it. cheers!Thallus
@Thallus this is critically wrong. You are supposed to use " angular.io/api/platform-browser/DomSanitizer#sanitize " for sanitizing incoming values. Everything you with "bypass..." is ok for debugging and prototyping but must be removed for production-grade code. I doubt that the mat-icon lib uses "bypass..." in the production code. But even if it is, that's just a flaw in the lib (as many libs have flaws) and nothing to take as reference.Johnie
@Johnie This is not for an incoming value, this is for our own icons. If you tend to include malicious code in your own icons, please, use a proper sanitizer. But for those of us who can trust our own icons from our own source, this is totally appropriate.Biforked
@Biforked doesn't matter "how much" you trust a source. Calling the sanitize-pipe, as it's meant to be, won't do any harm and is not more complicated. "bypassing" the sanitizing process is always "bad practice" except certain edge-cases where it's impossible to use the normal process. If you think, bypassing is ok "if you know what you do" then you need more experience to learn why that is never true. XSS is just one of many examples why you should never trust "your own icons" because it's sometimes not you who decides what "your own" icons means at runtime.Johnie
@Johnie I do agree that a sanitizing won't do any harm, and I also see how one could miss a hidden threat. Could you tho please explain in this situation how could XSS be a threat?Biforked
@Biforked if an attacker is able to provide a malicious image-path (by XSS) by some query-param etc for example, you are screwed if you are just "bypassing" the sanitizing process. The attacker can then inject js-code into your page ("XSS"). If you sanitize for an image, then everything that is NOT a valid image will be cleaned or blocked.Johnie
@Johnie I don't get it. Do they already have to commit XSS to be able to commit XSS by the malicious images? Why don't just use the first XSS then? Also, could you please expand on the query param bit?Biforked
@Johnie My whole uncertainty about your concern is that if an attacker is able to change a constant sting in my js (that is, the image path), what is there that they can not already do, but can do with a malicious image? If they have absolute control, how can more control hurt? Do I miss something?Biforked
@Biforked everyone can create a link to "your-app.com?search=abc" where "abc" is a search-string parameter and send it to someone. So this is a default way "someone" or an "attacker" can "control" what your application does to the user. An XSS is a way to use such params in a malicious way to have your app make something evil on a user's browser. To prevent this, Angular has implements "sanitizing" functions. If you just "bypass" them as developer, you are putting every user on risk without any pro-argument. "I know what I do" is showing too little experience in software-development.Johnie
@Johnie Sorry, but you failed to answer my question. You seem to be unable to argument your point here. I believe there is no way to use an XSS in any way to leverage the un-sanitised images in this particular case. And there is a pro argument, that is performance. I did agree with you in that sanitizing is a must in a general context, would you please put forward ANY scenario where it would be a problem not to do so in THIS use case?Biforked
@Biforked "performance" is no "pro-argument" in this case. Dive into how JS works under the hood (like "hot functions" etc). If you want to have performance, don't use a big chunky framework liek Angular in the first place. I perfectly argumented for why you should always consider security as important and what the security best practice is in this case. If you desperately want to avoid best practice, all good. But Stackoverflow serves many upcoming devs who will read this question/answer and will use it for infinite different cases. So it's not only about "you" here. /endJohnie
@Johnie I dont think Stackoverflow supports opinion based arguments like yours. Since you are unable to support your standpoint (except for deflecting), it is nothing but opinion. If people misuse this answer, that is on them. If you claim that this answer is dangerous when it is not, and you are unable to argue it, that is on you. And it is misleading. No need to get personal here.Biforked
@Johnie And I do agree that pointing out that this practice might be dangerous in most cases is necessary. But claiming that it is dangerous in this case is false.Biforked
sigh This is not the place to explain XSS or give detailed examples. The point is to stick to securit best-practices. In Angular it's 1 line of code to bypass the security and it's also 1 line to use the default security implementation. If you want to know what an img-XSS can do, just google. There are plenty of examples. If me not giving an example here in this thread is reason enough for you to use the unsafe bypassing instead of the safe version, go ahead. It's not up to me to provide out-of-scope examples specifically you.Johnie
D
19

Include your SVG files in src/assets folder and add the svg folder in your angular.json file.

"assets": [ "src/assets/svg/*" ]

This way you can include the file in your components as you wish.

Drida answered 30/10, 2018 at 14:56 Comment(2)
Thanks! One rather add svg folder "assets": [ "src/assets/svg/*" ] than each svg file.Waller
What if it is a lib? When I tried that and tried running the app that's in the same workspace and is using the lib, it gets the following error: An unhandled exception occurred: The projects/my-lib/src/assets asset path must start with the project source root. But if I start from src, there is no way it's going to know if I mean the lib or the app. Referring to the app's assets folder in the same way starting from projects works finePontiac
D
7

one way to do this is to set id property for your svg file and put your svg files in your asset folder. then use that id in mat-icon like this:

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

this is a better way to do it; in this way you don't have to deal with svg tags in your UI html code. also this supports google icons.

though this works if you're using angular material.

Edit: You need to register the icon with the IconRegistry in your component for this to work:

  constructor(iconRegistry: MatIconRegistry, sanitizer: DomSanitizer) {
    iconRegistry.addSvgIcon(
        'my-star-icon',
        sanitizer.bypassSecurityTrustResourceUrl('assets/icons/my-star-icon.svg'));
  }

Check the docs here and an example here.

Darceydarci answered 30/10, 2018 at 16:25 Comment(2)
This needs additional setup with Angular Material. Can you update your answer with those. If not, i can go ahead and edit your answer.Paulino
@OjasDeshpande you may edit my answer. I have not worked with the newer version. thanks for pointing it out.Darceydarci
S
2

There is a more elegant way, however it implies that the svg file has the same ID as the file name.

Svg component:

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;
    }
  }
}

Let's say you have svg in a folder: /assets/icons/person.svg

The svg itself contains the following code (So that you can easily change the size and color of your svg, it should not contain height, width and fill attributes):

<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>

And now you can use your icon in any component:

<app-svg-icon [icon]="'person'" fill="red" [size]="48"></app-svg-icon>
Seal answered 22/2, 2022 at 23:7 Comment(1)
I like this one. We've been using it in our apps. But now I need it to be part of a library and the library will have to provide he icons. I was only able to use icons present in the assets folder of the app and not the ones in the libPontiac
L
1

So I was trying to do this, and for the life of me i could not get this svg to show up until.... After many internet searches, and I don't think it's just me and if you copy pasta path from the internet then maybe you forgot to include this

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>

Then you can go ahead and do your

 <img src="assets/img/logo.svg" />

and if this doesn't work and somehow you want to work with images instead then put your images in

assets/img/copypasta.png

Lettyletup answered 10/4, 2021 at 14:58 Comment(0)
P
1

Use SVG as Template

You can do it directly by use SVG instead HTML as component template - more details here.

Working example here

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

inside this SVG template (which is in separate file!) you can use styles and variables in angular way

Putrefaction answered 8/2, 2023 at 15:42 Comment(0)
G
0

2024 version. The icon was not visible until I set the innerHtml programatically. Also beware of auth interceptors, adding headers to the get request can cause it to fail with empty error.

import { HttpClient } from '@angular/common/http';
import { ChangeDetectionStrategy, Component, ElementRef, HostBinding, Input, OnChanges, ViewChild, ViewEncapsulation } from '@angular/core';

@Component({
  selector: 'app-icon',
  template: `<span #svgContainer></span>`,
  changeDetection: ChangeDetectionStrategy.OnPush,
  encapsulation: ViewEncapsulation.None,
})
export class IconComponent implements OnChanges {

  @Input() src?: string;
  @ViewChild('svgContainer', { static: true }) svgContainerRef!: ElementRef;
  @HostBinding('style.display') display = 'inline-flex';

  constructor(private http: HttpClient) { }

  ngOnChanges() {
    if (!this.src) return;
    this.http.get(this.src, { responseType: 'text' }).subscribe((data) => {
      const div = this.svgContainerRef.nativeElement;
      if (div) {
        div.innerHTML = data;
      }
    });
  }
}

This component is then being used like this:

<app-icon [src]="icons.logout"></app-icon>

where icons are simple urls like this:

export const icons = {
  arrowRightO: 'assets/icons/arrow-right-o.svg',
  home: 'assets/icons/home-alt.svg',
  upload: 'assets/icons/software-upload.svg',
  logout: 'assets/icons/log-out.svg',
}
Georgie answered 3/3 at 23:34 Comment(0)
T
-1

Angular - create one more component, add a lot of stuff, etc and then get your svg.

React - just make an import of your svg and continue to work.

(and yes, it will be added as tag. You can fill it with css, change it whatever you like)

Compare React and Angular imort of SVG

Tectonics answered 14/2, 2023 at 16:8 Comment(3)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Navada
The Angular approach, based on this discussion, allows for server side generation of the SVG, and inclusion of that SVG into the DOM. This is not what the React Import and some of the solutions presented do. React Import and putting things in Asset folders places the SVG into the bundles, which may be what some people want, but for charts and things like that, the http method seems to be the best all around answer.Quinnquinol
Great news: according to this article we should now be able to import svgs through typescriptExuviate

© 2022 - 2024 — McMap. All rights reserved.