angularangular-directivegoogle-material-iconsmaterial-icons

Angular: Some Google Material icons not showing in browser


I am developing a custom icon library for my Angular project, as many of the icons in Font Awesome now are not free. I used Google Material Icons, implementing them as a stylesheet. Then I created an icon servcie and an icon directive for easy calling.

Here is my icon.service.ts:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
import { map } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class IconsService {
  constructor(private http: HttpClient, private sanitizer: DomSanitizer) {}

  getIcon(name: string): Observable<SafeHtml> {
    return this.http.get(`assets/icons/${name}.svg`, { responseType: 'text' }).pipe(
      map(svgString => this.sanitizer.bypassSecurityTrustHtml(svgString) as SafeHtml) // assert non-null
    );
  }
}

My icon directive where most work gets done:

import {Directive, ElementRef, Input, Renderer2} from '@angular/core';

@Directive({
  selector: '[icon]',
  standalone: true,
})
export class IconDirective {
  private _iconName: string = '';
  private _spin: boolean = false;
  private _size: string | undefined;
  private _flipHorizontal: boolean = false;
  private _flipVertical: boolean = false;
  private _color: string | undefined;

  @Input('icon')
  set icon(value: string) {
    const parts = value.split(' ');
    this._iconName = parts[0];

    this._spin = parts.includes('spin');

    this._flipHorizontal = parts.includes('flip-horizontal');

    this._flipVertical = parts.includes('flip-vertical');

    this._size = parts.find(part => part.match(/^[0-5]x$/)) || '1x'; // Default to 1x

    const colorPart = parts.find(part => part.startsWith('color-'));
    this._color = colorPart ? colorPart.split('-')[1] : undefined;

    this.updateIcon();
  }

  constructor(private el: ElementRef, private renderer: Renderer2) {
  }

  ngAfterViewInit(): void {
    this.setupLazyLoading();
  }

  private setupLazyLoading(): void {
    const options = {
      root: null, // observes changes in the viewport
      threshold: 0.01, // trigger when 1% of the element is visible
    };

    const observer = new IntersectionObserver((entries, observer) => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          this.updateIcon();
          observer.unobserve(entry.target); // Stop observing once loaded
        }
      });
    }, options);

    observer.observe(this.el.nativeElement);
  }

  private updateIcon(): void {
    console.log('Updating icon');
    // Clear previous classes
    this.el.nativeElement.className = '';

    // Set text content to the icon name
    this.renderer.setProperty(this.el.nativeElement, 'textContent', this._iconName);
    this.renderer.addClass(this.el.nativeElement, 'material-icons');

    // Apply size, spin, and flip classes
    if (this._spin) {
      this.renderer.addClass(this.el.nativeElement, 'spin');
    }
    if (this._flipHorizontal) {
      this.renderer.addClass(this.el.nativeElement, 'flip-horizontal');
    }
    if (this._flipVertical) {
      this.renderer.addClass(this.el.nativeElement, 'flip-vertical');
    }
    if (this._size) {
      this.renderer.addClass(this.el.nativeElement, `icon-${this._size}`);
    }
    if (this._color) {
      this.renderer.setStyle(this.el.nativeElement, 'color', this._color);
    }
  }
}

My html at this point:

<h1>Icon examples</h1>
<h2>Sizing: <i icon="search"></i><i icon="search 2x"></i><i icon="search 3x"></i></h2>
<h2>Spinning: <i icon="search spin"></i></h2>
<h2>Flipping horizontally and vertically: <i icon="search flip-horizontal"></i><i icon="search flip-vertical"></i></h2>
<h2>Colors: <i icon="search color-blue"></i><i icon="search color-red"></i><i icon="search color-green"></i></h2>
<h1>Button example: <button><i icon="donut_large spin"></i>Loading</button></h1>
<button><i icon="settings 2x"></i></button>
<button><i icon="menu 2x"></i></button>
<!--<button><i icon="stacks 2x"></i></button>-->
<button><i icon="print 2x"></i></button>
<button><i icon="open_with 2x"></i></button>
<button><i icon="add 2x"></i></button>
<button><i icon="public 2x"></i></button>
<!--<button><i icon="captive_portal 2x"></i></button>-->
<button><i icon="filter_alt 2x"></i></button>
<button><i icon="filter_alt_off 2x"></i></button>
<button><i icon="arrow_forward_ios 2x"></i></button>
<button><i icon="arrow_back_ios 2x"></i></button>
<button><i icon="pan_tool 2x"></i></button>
<button><i icon="progress_activity 2x"></i></button>

But this is what I can see on screen, some icons like progress activity are not rendering, what am I doing wrong?

last icon not visible/rendering

I tried changing browser, changing my css to include font family.. Nothing.

 /*Icon base styles */
[icon] {
  display: inline-block;
  font-size: 16px;
  padding: 2px;
  fill-opacity: 0;
  vertical-align: middle;
  horiz-align: center;
}

 @font-face {
   font-family: 'Material Icons';
   font-style: normal;
   font-weight: 400;
   src: url(https://example.com/MaterialIcons-Regular.eot); /* For IE6-8 */
   src: local('Material Icons'),
   local('MaterialIcons-Regular'),
   url(https://example.com/MaterialIcons-Regular.woff2) format('woff2'),
   url(https://example.com/MaterialIcons-Regular.woff) format('woff'),
   url(https://example.com/MaterialIcons-Regular.ttf) format('truetype');
 }


/* Icon size variations */
@keyframes spin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

.spin {
  animation: spin 2s infinite linear;
  padding: 2px;
}

 .flip-horizontal {
   transform: scaleX(-1);
 }

 .flip-vertical {
   transform: scaleY(-1);
 }

 .flip-left {
   transform: rotateY(180deg);
 }

 .flip-right {
   transform: rotateY(-180deg);
 }

 .material-icons.icon-1x { font-size: 16px; } /* Default Material Icons size */
 .material-icons.icon-2x { font-size: 32px; }
 .material-icons.icon-3x { font-size: 48px; }
 .material-icons.icon-4x { font-size: 64px; }

Solution

  • I can see that [progress_activity](https://fonts.google.com/icons?icon.query=progress_activity) is in the Material Symbols collection but not in the Material Icons collection.

    My initial guess is that you followed the instructions to install Material Icons and if you want additional iconography like progress_activity you should follow the directions for Material Symbols instead.