Components: Error when retrieving icon from registry in server side rendering

Created on 1 Feb 2018  路  19Comments  路  Source: angular/components

Bug, feature request, or proposal:

When we register some icons in MatIconRegistry and access to them in SSR, we have some errors and icons aren't displayed.

What is the expected behavior?

Display icons and not have error in the console

What is the current behavior?

Icon aren't displayed and we have error in the console

What are the steps to reproduce?

Create a component and register an icon in MatIconRegistry:

import {Component, OnInit} from '@angular/core';
import {MatIconRegistry} from '@angular/material';
import {DomSanitizer} from '@angular/platform-browser';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {

  constructor(private _matIconRegistry: MatIconRegistry, private _domSanitizer: DomSanitizer) {
    this._matIconRegistry.addSvgIcon('loader', this._domSanitizer.bypassSecurityTrustResourceUrl('assets/Icons/loader-default.svg'));
  }

  ngOnInit() {
  }

}
@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    // Make AppModule compatible with Universal
    BrowserModule.withServerTransition({ appId: 'universal-app' }),
    MatIconModule,
    HttpClientModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule {
}

Access to it in server side rendering:

<mat-icon svgIcon="loader"></mat-icon>

In the console you'll have this message:

Error retrieving icon:
ERROR [Error]

What is the use-case or motivation for changing an existing behavior?

MatIconRegistry should work in SSR

Which versions of Angular, Material, OS, TypeScript, browsers are affected?

"dependencies": {
    "@angular/animations": "^5.2.2",
    "@angular/cdk": "^5.1.1",
    "@angular/common": "^5.2.2",
    "@angular/compiler": "^5.2.2",
    "@angular/core": "^5.2.2",
    "@angular/forms": "^5.2.2",
    "@angular/http": "^5.2.2",
    "@angular/material": "^5.1.1",
    "@angular/platform-browser": "^5.2.2",
    "@angular/platform-browser-dynamic": "^5.2.2",
    "@angular/platform-server": "^5.2.2",
    "@angular/router": "^5.2.2",
    "@hapiness/config": "^1.1.1",
    "@hapiness/core": "^1.3.0",
    "@hapiness/ng-universal": "^5.2.2",
    "@hapiness/ng-universal-transfer-http": "^5.2.2",
    "@nguniversal/module-map-ngfactory-loader": "^5.0.0-beta.5",
    "core-js": "^2.5.1",
    "rxjs": "^5.5.6",
    "zone.js": "^0.8.20"
  },
  "devDependencies": {
    "@angular/cli": "^1.6.6",
    "@angular/compiler-cli": "^5.2.2",
    "@angular/language-service": "^5.2.2",
    "@types/jasmine": "~2.8.3",
    "@types/jasminewd2": "~2.0.2",
    "@types/node": "^9.4.0",
    "codelyzer": "^4.1.0",
    "jasmine-core": "~2.8.0",
    "jasmine-spec-reporter": "~4.2.1",
    "karma": "~2.0.0",
    "karma-chrome-launcher": "~2.2.0",
    "karma-coverage-istanbul-reporter": "^1.2.1",
    "karma-jasmine": "~1.1.0",
    "karma-jasmine-html-reporter": "^0.2.2",
    "protractor": "~5.1.2",
    "ts-loader": "^3.3.1",
    "ts-node": "^4.0.1",
    "tslint": "^5.9.1",
    "typescript": "^2.6.2"
  }
P4 materiaicon help wanted

Most helpful comment

For SSR, full URL is needed in order to retrieve svg icon.
When encountered to this issue, I fixed it with something like this:

import {Component, OnInit, Inject} from '@angular/core';
import {MatIconRegistry} from '@angular/material';
import {DomSanitizer} from '@angular/platform-browser';
import { PLATFORM_ID } from '@angular/core';
import { isPlatformBrowser, isPlatformServer } from '@angular/common';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {

  constructor(private _matIconRegistry: MatIconRegistry, private _domSanitizer: DomSanitizer, @Inject(PLATFORM_ID) private platformId: string) {
    const svgUrl = 'assets/Icons/loader-default.svg';
    // domain and port for SSR in this example is static. Use i.e. environment files to use appropriate dev/prod domain:port
    const domain = (isPlatformServer(platformId)) ? 'http://localhost:4000/' : ''; 
    this._matIconRegistry.addSvgIcon('loader', this._domSanitizer.bypassSecurityTrustResourceUrl(domain + svgUrl));
  }

  ngOnInit() {
  }

}

All 19 comments

Anyone here ?

Having the same probleam here!

So bad but for them it鈥檚 only a minor issue even if universal is one the most important feature in angular

For SSR, full URL is needed in order to retrieve svg icon.
When encountered to this issue, I fixed it with something like this:

import {Component, OnInit, Inject} from '@angular/core';
import {MatIconRegistry} from '@angular/material';
import {DomSanitizer} from '@angular/platform-browser';
import { PLATFORM_ID } from '@angular/core';
import { isPlatformBrowser, isPlatformServer } from '@angular/common';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {

  constructor(private _matIconRegistry: MatIconRegistry, private _domSanitizer: DomSanitizer, @Inject(PLATFORM_ID) private platformId: string) {
    const svgUrl = 'assets/Icons/loader-default.svg';
    // domain and port for SSR in this example is static. Use i.e. environment files to use appropriate dev/prod domain:port
    const domain = (isPlatformServer(platformId)) ? 'http://localhost:4000/' : ''; 
    this._matIconRegistry.addSvgIcon('loader', this._domSanitizer.bypassSecurityTrustResourceUrl(domain + svgUrl));
  }

  ngOnInit() {
  }

}

I go this error instead error retrieving icon: <svg> tag not found and absolute path doesn't solve this issue. I have searched through the internet and could barely find any solution or workaround for it, please help give me any hint if you can, thanks a lot

Hi there, do not take it wrong but what is the advantage of using mat-icon compared to img ?

With universal even using the method above I can see a flash of content forthe svg using I am not too sure also about the cache mecanism used by mat-icon , thninking that there is none actually.

And I just spend 2hours on a bug linked to the solution above, unfortunatly no time to investiguate the real root issue behind as it s lie behind a lot of complexity with sessions, cookies and http interceptor server and session side.

I am just going to forget mat-icon for a while and use the native img for now...

A pity, but no choice I believe we need to be productive and use the right solution for the right app, in the mean time if some one can enlight me with why mat-icon would be abetter choice I am happy to hear and maybe finding the time to investiguate mat-icon solution more.

Thank you guys

Exemple of good arguments are:

Changing the SVG color.

I am facing the same problem with angular universal (prerendering)

Reproduction:

  1. Clone @angular-material-extensions/freelancer-theme

  2. cd freelancer-theme && npm i

  3. npm run build:prerender

Error retrieving icon: 
Error retrieving icon: 
Error retrieving icon: 
Error retrieving icon: 

Process finished with exit code 0

Note: I guess that the base-href cli arg doesn't take any effect with MatIconRegistry

Got simular issue, but the icon appers suddenly after a few refreshes.

Just follow this receipt to use absolute paths on server side:
https://angular.io/guide/universal#using-absolute-urls-for-server-requests

If you don't need icons to be actually rendered by server you can just replace icons with epmty SVG

  private registerIcon(name: string, filename: string) {
    if (isPlatformServer(this.platformId)) {
      /* Register empty icons for server-side-rendering to prevent errors */
      this.matIconRegistry.addSvgIconLiteral(name, this.domSanitizer.bypassSecurityTrustHtml('<svg></svg>'));
    } else {
      this.matIconRegistry.addSvgIcon(name, this.domSanitizer.bypassSecurityTrustResourceUrl(`assets/path/to/icons/${filename}.svg`));
    }
  }

In this case all icons will be empty in server response but frontend will load them after loading

Even this example isn't working.

https://stackblitz.com/angular/ygonjmkebrr

Seconding the concern something is up with mat-icons and svgs, even the angular material examples are broken

https://stackblitz.com/angular/yjprlknbxmgk?file=src%2Fapp%2Ficon-svg-example.html

For me it was the path to the icons themselves when using DomSanitiser. I changed my path "../assets/icon" to point directly to the assets folder "assets/icon", because it seems that the compiler does not automatically fix this during production.

Just run into this same problem! And it has nothing to do with server side!

here's the one I opened - https://github.com/angular/components/issues/19767. Might be related.

I ran into this issue as well and couldn't solve my problem until I realized that I had an interceptor that was changing all outbound request headers to application/json.
I fixed it by adding some extra logic to see if I was requesting a resource from the assets folder to fix it.
If any of you are still having this issue you might look at that.

For SSR, full URL is needed in order to retrieve svg icon.
When encountered to this issue, I fixed it with something like this:

import {Component, OnInit, Inject} from '@angular/core';
import {MatIconRegistry} from '@angular/material';
import {DomSanitizer} from '@angular/platform-browser';
import { PLATFORM_ID } from '@angular/core';
import { isPlatformBrowser, isPlatformServer } from '@angular/common';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit {

  constructor(private _matIconRegistry: MatIconRegistry, private _domSanitizer: DomSanitizer, @Inject(PLATFORM_ID) private platformId: string) {
    const svgUrl = 'assets/Icons/loader-default.svg';
    // domain and port for SSR in this example is static. Use i.e. environment files to use appropriate dev/prod domain:port
    const domain = (isPlatformServer(platformId)) ? 'http://localhost:4000/' : ''; 
    this._matIconRegistry.addSvgIcon('loader', this._domSanitizer.bypassSecurityTrustResourceUrl(domain + svgUrl));
  }

  ngOnInit() {
  }

}

Facing same issue, with version 10.0.2 of @nguniversal/express-engine. @milosbr solution works indeed, however reading angular's doc about absolute urls, we shouldn't have to do this, unless I'm missing something...

If you are using one of the @nguniversal/*-engine packages (such as @nguniversal/express-engine), this is taken care for you automatically. You don't need to do anything to make relative URLs work on the server.

@JGGentzel Do you have a way I can contact you by? I'd need some help in figuring out how to fix the interceptor issue.

Seconding the concern something is up with mat-icons and svgs, even the angular material examples are broken

https://stackblitz.com/angular/yjprlknbxmgk?file=src%2Fapp%2Ficon-svg-example.html

I'm also getting the error <svg> tag not found at MatIconRegistry... with just a regular non-SSR Angular project. Like @aparroccini said, even the example in the docs has the same error. Anyone know how to fix?

Was this page helpful?
0 / 5 - 0 ratings