Eydrian
Eydrian

Reputation: 10828

How to inline svgs in angular?

I'm trying to load svgs inline with angular 7.

So far I tried:

import icon = require('./icon.svg');

results in icon.svg due to file-loader

import icon = require('raw-loader?!./icon.svg');

results in __webpack_public_path__ + "icon.svg";

which is the same as:

import * as icon3 from 'raw-loader?!./icon.svg';

and

import icon4 from 'raw-loader?!./icon.svg';

will become undefined.

However renaming the icon.svg in something like icon.foo and then loading the icon with:

import * as icon from 'raw-loader?!./icon.foo';

and the appropriate type in typings.d.ts results in the anticipated behavior and the variable icon holds the inlined content.

For me it seams like the file-loader somehow precedes the raw loader. Changing node_modules/@angular-devkit/build-angular/src/angular-cli-files/models/webpack-configs/common.js to load svgs like htmls in the rules works as well. But this is not a way to go.

Any ideas?

Upvotes: 4

Views: 8618

Answers (3)

Dominik Ehrenberg
Dominik Ehrenberg

Reputation: 1756

2025 answer, kinda

If you do not want to use any additional loaders, you can actually use SVG files as templates for components:

@Component({
  selector: 'app-some-svg',
  templateUrl: './some-svg.svg',
  styleUrls: ['./some-svg.scss'],
})

If you want to be able to control the SVG from the outside (for example because you want to change colors), you can use @HostBinding for that.

Example: Let's try to control the fill color of a svg.

Given the following SVG:

<svg xmlns="http://www.w3.org/2000/svg" width="54" height="54" viewBox="0 0 54 54">
  <circle class="circle" cx="27" cy="27" r="27" fill="#7df481" />
</svg>

You can write the controller for it like so:

type FlexibleFillColor = 'green' | 'red';

@Component({
  selector: 'app-flexible-fill',
  templateUrl: './flexible-fill.svg',
  styleUrls: ['./flexible-fill.scss'],
})
export class FlexibleFillComponent implements OnInit, OnChanges {
  @Input() color: FlexibleFillColor = 'grey';

  @HostBinding('class') className = this.color;

  ngOnInit() {
    this.className = this.color;
  }

  ngOnChanges(changes: SimpleChanges) {
    this.className = changes.color.currentValue as FlexibleFillColor;
  }
}

Since @HostBinding is not automatically updated when the @Input value changes, the OnChanges interface is needed, where the value can then be updated

The SCSS for it would look something like this:

:host {
  &.green {
    .circle {
      fill: green;
    }
  }

  &.red {
    .circle {
      fill: red;
    }
  }

  svg {
    width: 32px;
    height: 32px;
    margin: 1.5em 0 0 0.5em;
  }
}

If necessary you can also use all this to show and hide elements in the SVG

Upvotes: 0

Eydrian
Eydrian

Reputation: 10828

Because the rule is already defined, it has to be overruled by putting two !! at the beginning of the loader:

import icon = require('!!raw-loader?!./icon.svg');

ADDITION 2021

it should work without require ... do not forget the typings.d.ts

import icon from '!!raw-loader?!./icon.svg';

See: https://webpack.js.org/loaders/raw-loader/ at the very bottom

Beware, if you already define loader(s) for extension(s) in webpack.config.js you should use:

'!!raw-loader!./file.css'; // Adding !! to a request will disable all loaders specified in the configuration

Upvotes: 12

joka00
joka00

Reputation: 2537

I would suggest you to create a .ts file

my-svg.ts

export const mySVG = `<svg></svg>`;

Then you can import it normally

import {mySVG} from 'path/to/file/my-svg.ts'

Upvotes: 1

Related Questions