Using Mat-Icon Component – Part Two

Blog Post #002

Duncan Faulkner – January 2020

I hadn’t intended to write about this just yet, but I got a chance to investigate this sooner than I had thought. I was refactoring part of a project that was using the Mat-Icon component, with an icon.module.ts file as I’d discussed in the previous post.

I wanted to see if I could improve on the code and reduce the amount of repeated code by iterating over a file and registering the icons.

I started with a JSON file and populated it with some image names and added the HttpClientModule to use an http.get() method to read the JSON file. All good so far, the file is reading and looping with image names being returned and passed into the matIconRegistery.addSvgIcon() method.

I thought I might get some issues, but it all seemed to work fine, except the application was erroring, the issue was that the application was loading before the call to the JSON file had returned with the results, so even though I was getting images and registering icons, the app had already tried to use the icons and they weren’t there at that point, and the images were being returned after the app had started.

OK, so may be this is a timing issue and it doesn’t work in the icon.module.ts file, so I moved code to the app.component.ts file. Nope, the results were the same. This was not the result I was expecting and it was a little odd because the icon.module.ts file was loading images before.

I’m going to revisit this at a later date, as I would like to understand what the issue is and why it was behaving the way it did, my guess is it has something to do with the http.get() request and the timing around that.

But as I needed to get this refactoring completed I needed to look for another solution.

After a bit of research I found an answer and that was to create a service to handle the iterating and registering of icons. Instead of a JSON file to store a list of images the solution was an enum, with a key value structure.

export enum Icons {
    add = 'add',
    power = 'power',
    save = 'save'
}

To keep things simple I’ve made the key and the image name the same.

The service is quite simple:

// imports
import { Icons } from '../image.enum';

@Injectable({ providedIn: 'root'})
export class IconService {
  constructor(
     private matIconRegistry: MatIconRegistry,
     private sanitizer: Domsanitizer
){}

registerIcons(): void {
   this.load(Object.values(Icons), 'assets/images/icons');
}

private load(key: string[], url: string): void {
   key.forEach(icon => {
     this.matIconRegistry.addSvgIcon(
       icon, 
       this.sanitizer.bypassSecurityTrustResourceUrl(`${url}/${icon}.svg`)
   ); 
 });
 }
}

And on the app.component.ts file call the registerIcons() function.

// imports
import { IconService } from './icon.service';

export class AppComponent implements OnInit {
   constructor(private iconService: IconService) {
      this.iconService.registerIcons();
   }

}

And that’s it, not what I had in mind when I started this, but it does deliver what I wanted to do, and that was to reduce the amount of code to register icons. Though I guess the enum could get pretty lengthy if there are a lot of images.

I have to thank Stefanos Kouroupis -dev.to – for his article and code on this as I wouldn’t have thought about using a service for this and this has changed my thinking. I wanted to include here it as a follow up to my previous post for completeness, you can see his full article here https://dev.to/elasticrash/using-custom-made-svg-icons-through-the-angular-material-library-2pif.

I will add a github repo of the code in a day or two.


Previous posts…