mr_blond
mr_blond

Reputation: 1730

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

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?

Upvotes: 43

Views: 123850

Answers (10)

Andreu
Andreu

Reputation: 1

Coudn't make it work with any solution. Finally came up with this:

import { CommonModule } from '@angular/common';
import { HttpClient } from '@angular/common/http';
import { Component, ElementRef, Input, OnInit } from '@angular/core';

@Component({
  selector: 'app-svg-icon',
  standalone: true,
  imports: [CommonModule],
  template: `<div class="svg-container" [ngClass]="this.class"></div>`
})
export class SvgIconComponent implements OnInit {
  @Input() icon: string = '';
  @Input() class: string = '';

  constructor(
    private http: HttpClient,
    private el: ElementRef
  ) {}

  ngOnInit(): void {
    this.loadSvg();
  }

  loadSvg(): void {
    if (this.icon) {
      this.http.get(`assets/icons/${this.icon}.svg`, { responseType: 'text' }).subscribe(
        svgContent => {
          this.insertSvgContent(svgContent);
        },
        error => {
          console.error('Error loading SVG:', error);
        }
      );
    }
  }

  insertSvgContent(svgContent: string): void {
    const svgContainer = this.el.nativeElement.querySelector('.svg-container');
    svgContainer.innerHTML = svgContent; // Inject SVG content into the container

    const svgElement = svgContainer.querySelector('svg');
    if (svgElement) {
      svgElement.classList.add('loaded-svg'); // Add a class for styling purposes
    }
  }
}

And use it like:

<app-svg-icon class="h-4 w-4 text-red-700" icon="trash-bin"></app-svg-icon>

In this case i'm using tailwind to style my svg's

Upvotes: 0

Kamil Kiełczewski
Kamil Kiełczewski

Reputation: 92377

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

Upvotes: 4

JanBrus
JanBrus

Reputation: 1482

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',
}

Upvotes: 0

Spawnet
Spawnet

Reputation: 47

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

Upvotes: 0

jsnewbie
jsnewbie

Reputation: 1819

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>

Upvotes: 23

Venom
Venom

Reputation: 155

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>

Upvotes: 3

youllbehaunted
youllbehaunted

Reputation: 649

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

Upvotes: 1

Arya11
Arya11

Reputation: 568

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.

Upvotes: 7

Boris Yakubchik
Boris Yakubchik

Reputation: 4453

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

Upvotes: 48

Harini P
Harini P

Reputation: 627

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.

Upvotes: 19

Related Questions