3 methods to retrieve an asset in Angular CLI

3 methods to retrieve an asset in Angular CLI

This post was inspired by a question on StackOverflow about loading (content) of an SVG file in Angular. In general, about retrieving the content of any file.

The problem

There was an SVG file in an Angular project that the OP wanted to include in a component:

TypeScript
app.component.ts
HTML
app.component.html

Surprisingly, nearly every approach worked in StackBlitz, even if it was reported as an error. Unfortunately, they stopped working in a local Angular CLI project. This is one example. It’s not possible to import anything from logo.svg because logo.svg is not a module that exports anything:

error TS2307: Cannot find module '../assets/logo.svg'.

I had three suggestions for solving this problem.

Use it as an image source

Assuming that the SVG is known at the compilation time, it is possible to use the SVG file as any other image (PNG, JPEG) in HTML or CSS. For example, that <div> could have set background-image to the SVG file, or there could be simply an <img> tag with the icon:

HTML
app.component.html
CSS
app.component.scss

Refer to my other article for more examples and details on importing assets.

Apparently, the SVG file in the example was invalid so it wasn’t immediately displayed using these methods. Its <svg> tag was:

XML
logo.svg

while it should contain the namespaces:

XML
logo.svg

After adding the missing information, the logo was shown.

Load dynamically using AJAX

If static loading is not the case, you may load the file dynamically using an AJAX web request. This method involves the use of HttpClient.get method:

TypeScript
app.component.ts

The responseType: 'text' is necessary because the default value of responseType is json. A JSON parser trying to load an SVG file will throw an error such as:

"SyntaxError: Unexpected token < in JSON at position 0
    at JSON.parse (<anonymous>)
    at XMLHttpRequest.onLoad (http://localhost:4200/vendor.js:7932:51)

Naturally, the error

NullInjectorError: No provider for HttpClient!

means that the HttpClientModule was not imported by the module containing your component. This can be quickly fixed by adding:

TypeScript
app.module.ts

Require the file

The last method is a bit hacky, as it includes the Node’s require function:

TypeScript
app.component.ts

If launched without further configuration, you will see a strange error:

ERROR in src/app/app.component.ts(4,14): error TS2580: Cannot find name 'require'. Do you need to install type definitions for node? Try `npm i @types/node` and then add `node` to the types field in your tsconfig.

Do as suggested – add the @types/node typings to your project by running npm install @types/node and edit tsconfig.app.json to set:

TypeScript
tsconfig.app.json

In previous versions of Angular, where you used own Webpack configuration, this solution could work, depending on your configuration. In Angular CLI it should not work.

Angular 7 CLI

If you take a look at the Angular’s Webpack configuration (node_modules\@angular-devkit\build-angular\src\angular-cli-files\models\webpack-configs\common.js), you will find the following lines:

JS
…/webpack-configs/common.js

As you can see, the svg extension is handled with file-loader. According to the documentation, it returns URL to the file and copies the file to the output directory. That’s why require('../assets/logo.svg') returned logo.svg – path to the output file.

In order to get the contents of the file, we have to use the raw-loader handler, which allows importing files as a String. Using the syntax from this manual solves our problem:

TypeScript
app.component.ts

More about this particular syntax can be found in the Webpack documentation. Briefly, !! disables any configured loaders and ! separates the loader from the path.

Angular 8 CLI

The previous method used in Angular 8 results in a surprising result – the output is a Module:

Module {default: "<svg viewBox="0 0 104 104" version="1.1" xmlns=…EFF" stroke-width="4" fill="#00FF98" /></svg>", __esModule: true, Symbol(Symbol.toStringTag): "Module"}
default: "<svg viewBox="0 0 104 104" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
  <circle cx="52" cy="52" r="50" stroke="#003EFF" stroke-width="4" fill="#00FF98" />
</svg>
"
Symbol(Symbol.toStringTag): "Module"

This is caused by using newer raw-loader module. You can find the detailed commit here:

-  return `module.exports = ${json}`;
+  return `export default ${json}`;

One solution is extracting the default member:

TypeScript
app.component.ts

Another, recommended way, is dropping the require support (and Node typings) and using the import keyword:

TypeScript
app.component.ts

Leave a Reply

avatar
  Subscribe  
Notify of