1

I'm pivoting a react app to angular. I had a component that would take an input prop and use that to assemble the icon it needed, sometimes using several different elements. The issue I'm running into is that angular is complaining when I try to assign elements to variables in the way I'm accustomed to in React.

React component:

import React from 'react';

import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';

import { faShareAlt, faBars, faClipboardCheck, faEnvelope, faExchangeAlt, faUndoAlt, faPlus, faMinus, faQuestion, faTimes, faCheck } from '@fortawesome/free-solid-svg-icons';
import { faEdit, faClipboard, faCopy, faClone, faSquare } from '@fortawesome/free-regular-svg-icons';
import { faTwitter, faFacebookF, faRedditAlien, faTumblr, faPinterest, faInstagram } from '@fortawesome/free-brands-svg-icons';

export default class RosterIcon extends React.Component {

  render(){
    let icons = {
      copy: <FontAwesomeIcon icon={faCopy} />,
      reset: <FontAwesomeIcon icon={faUndoAlt} transform='shrink-2' />,
      menu: <FontAwesomeIcon icon={faBars} />,
      question: <span className="fa-layers fa-fw"><FontAwesomeIcon icon={faSquare}/><FontAwesomeIcon icon={faQuestion} transform="shrink-6" /></span>,
      confirm: <span className="fa-layers fa-fw"><FontAwesomeIcon icon={faSquare}/><FontAwesomeIcon icon={faCheck} transform="shrink-6" /></span>,
      cancel: <span className="fa-layers fa-fw"><FontAwesomeIcon icon={faSquare}/><FontAwesomeIcon icon={faTimes} transform="shrink-6" /></span>,
      clone: <span className="fa-layers fa-fw"><FontAwesomeIcon icon={faClone}/><FontAwesomeIcon icon={faPlus} transform="shrink-6 right-1.5 up-1.5" /></span>,
      add: <span className="fa-layers fa-fw"><FontAwesomeIcon icon={faSquare}/><FontAwesomeIcon icon={faPlus} transform="shrink-6" /></span>,
      delete: <span className="fa-layers fa-fw"><FontAwesomeIcon icon={faSquare}/><FontAwesomeIcon icon={faMinus} transform="shrink-6" /></span>,
      switch: <FontAwesomeIcon icon={faExchangeAlt} />,
      share: <FontAwesomeIcon icon={faShareAlt} transform='shrink-2' />,
      edit: <FontAwesomeIcon icon={faEdit} />,
      email: <FontAwesomeIcon icon={faEnvelope} size='xs' />,
      clipboard: <FontAwesomeIcon icon={faClipboard} />,
      clipped: <FontAwesomeIcon icon={faClipboardCheck} />,
    }
    return(
      icons[this.props.icon]
    );

  }

}

Angular component (so far):

import { Component, Input, OnInit } from '@angular/core';

import { faShareAlt, faBars, faClipboardCheck, faEnvelope, faExchangeAlt, faUndoAlt, faPlus, faMinus, faQuestion, faTimes, faCheck } from '@fortawesome/free-solid-svg-icons';
import { faEdit, faClipboard, faCopy, faClone, faSquare } from '@fortawesome/free-regular-svg-icons';
import { faTwitter, faFacebookF, faRedditAlien, faTumblr, faPinterest, faInstagram } from '@fortawesome/free-brands-svg-icons';

@Component({
  selector: 'app-rostericon',
  template: `<fa-icon [icon]="icons[icon]"></fa-icon>`,
  styleUrls: ['./rostericon.component.scss']
})
export class RostericonComponent implements OnInit {
  @Input() icon:string;
  icons;
  constructor() { }

  ngOnInit(): void {
    this.icons = {
      copy: faCopy,
    }
  }

}

I'm wondering if it's possible to mimic the functionality of the react component, where I define the element(s) to be held in the variable object, and then easily render that as the template. (Also would love to be able to render the component(s) without [innerHTML] on a superfluous element)

Edit: The solution (thanks @The James. I can even use the default case statement to handle icons from the other font I plan to use):

import { Component, Input, OnInit } from '@angular/core';

import { faShareAlt, faBars, faClipboardCheck, faExchangeAlt, faUndoAlt, faPlus, faMinus, faQuestion, faTimes, faCheck } from '@fortawesome/free-solid-svg-icons';
import { faEdit, faClipboard, faCopy, faClone, faSquare } from '@fortawesome/free-regular-svg-icons';
import { faTwitter, faFacebookF, faRedditAlien, faTumblr, faPinterest, faInstagram } from '@fortawesome/free-brands-svg-icons';

@Component({
  selector: 'app-rostericon',
  template: `
  <fa-icon *ngIf="iconData?.length === 1" [icon]="iconData[0].icon" [transform]="iconData[0].transform"></fa-icon>
  <span *ngIf="iconData?.length > 1" class="fa-layers fa-fw">
    <fa-icon *ngFor="let icon of iconData" [icon]="icon.icon" [transform]="icon.transform"></fa-icon>
  </span>
`,
  styleUrls: ['./rostericon.component.scss']
})
export class RostericonComponent implements OnInit {
  @Input() icon:string;
  iconData:Array<Object>;
  constructor() { }

  ngOnInit(): void {
    switch (this.icon) {
      case "copy":
          this.iconData = [
            {
              icon: faCopy,
              transform: '',
            },
          ]
          break;
      case "question":
          this.iconData = [
            {
              icon: faSquare,
              transform: '',
            },
            {
              icon: faQuestion,
              transform: 'shrink-6',
            },
          ]
          break;
      default:
        break;
    }
  }
}
7
  • you shouldn't have to import all that stuff with angular.just run this from the command line one time and then you will have access to all your font awesome icons on any html page: npm i font-awesome --save-dev Commented Aug 18, 2020 at 0:11
  • @Rick not according to the font-awesome angular docs. I mean, maybe if I want to just import the font alone, but I'm assured by the docs that there are other functionality when using the components. :) Commented Aug 18, 2020 at 0:56
  • hmmm I'm not sure. I use font awesome of every page of every one of my angular apps and I have never imported anything like that. Commented Aug 18, 2020 at 1:51
  • @Rick I mean, I know you can use it as a font, just like any other font on the web. But is it easy to do transformations, masking, layering, etc, as the component offers? npmjs.com/package/@fortawesome/angular-fontawesome Commented Aug 18, 2020 at 3:23
  • yeah I see that documentation, but still doesn't make much sense to me. in all my projects I do a simple npm command on the first day I create my project, then never import anything else anywhere. I just add html tags like this to any file in my app and it works perfectly: <i class="fa fa-check"></i> Commented Aug 18, 2020 at 4:17

1 Answer 1

1

The react variables are holding a JSX object (not html) to be inserted into a virtual DOM.

Angular doesn't use a virtual dom and if you were to do something similar, (at best) you'd be holding an in-memory reference to a DOM object and you'd break garbage collection.

Angular uses templates of actual html with code replacements like handlebars does. So, instead of creating a collection like in React, just add each icon to the template etc etc etc

This should easily handle most of your cases. (forgive any typos as I didn't test anything)

import { Component, Input, OnInit } from '@angular/core';
    
@Component({
  selector: 'app-rostericon',
  template: `
      <i *ngIf="!layered" class="fa" [class]="iconClass" [style]="customStyle"></i>
      <span *ngIf="layered" className="fa-layers fa-fw">
          <i class="fa" [class]="layer1"></i>
          <i class="fa" [class]="layer2" style="transform: shrink-6"></i>
      </span>
  `,
  styleUrls: ['./rostericon.component.scss']
})
export class RostericonComponent implements OnInit {
  @Input() icon:string;
  customStyle: any = {};
  iconClass = "";
  layer1 = "";
  layer2 = "";
  layered: boolean = false;
  constructor() { }

  ngOnInit(): void {
    switch (icon) {
        //example of how to handle the case
        case "copy":
            customStyle = { fontColor: red };
            iconClass = "fa-copy";
            break;
        case "reset":
            customStyle = { fontColor: red };
            iconClass = "fa-undo-alt";
            break;
        case "question":
            layered = true;
            layer1 = "fa-square";
            layer2 = "fa-question";
            break;
        case ...
        case ...
        case ...
    }
  }

}
Sign up to request clarification or add additional context in comments.

3 Comments

So I need a template with a ton of *ngIF directives to be able to render the pieces I need based on the input string?
no. change your template to <i class="fa" [class]="icon"></i>. if you require different styles for each icon string, change it to <i class="fa" [class]="icon" [style]="customStyle"></i> and create a switch statement in the ngOnit or setter for the icon input to populate a variable called customStyle based up the icon string.
Ah, breaking up cases by their general pattern, I see. Will have to think a bit on how to apply the various transformations but I think I can make it work.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.