FortAwesome / Font-Awesome

The iconic SVG, font, and CSS toolkit
https://fontawesome.com
Other
73.72k stars 12.19k forks source link

Uncaught TypeError: Cannot read property 'icon' of undefined #12278

Closed bschwartz757 closed 5 years ago

bschwartz757 commented 6 years ago

Hello,

I have an existing project using Bootstrap 3.3.7 and was using Fontawesome 4.7 until I attempted to upgrade to 5 today. The icons are currently rendering, but are causing a TypeError that's killing my page.

I'm doing SSR (using Pug), but including fontawesome in JS script:

var fontawesome = require('@fortawesome/fontawesome')
var faBrands = require('@fortawesome/fontawesome-free-brands')
var faSolid = require('@fortawesome/fontawesome-free-solid')
var faRegular = require('@fortawesome/fontawesome-free-regular')
fontawesome.library.add(faBrands)
fontawesome.library.add(faSolid)
fontawesome.library.add(faRegular)

and I'm getting the following error in the browser: Uncaught TypeError: Cannot read property 'icon' of undefined

This seems to be causing the error (inside the js bundle):

function define(prefix, icons) {
  var normalized = Object.keys(icons).reduce(function (acc, iconName) {
    var icon = icons[iconName];
    var expanded = !!icon._icon_ [this is throwing the error, icon.icon doesn't seem to exist];

I'm using jQuery, but read https://fontawesome.com/how-to-use/svg-with-js#with-jquery. My <i> tags are all nested within a tags, and inspecting the DOM elements it looks like the svg replacement is happening fine and leaves the a tags intact. I do have a click listener attached to one of the a tags, but that shouldn't matter since the a tag isn't changed.

Any ideas?

Evanslooten commented 6 years ago

I have the same issue with a Laravel application using Backpack. The icons render mostly correctly, with the fa-angle-lefts on my sidebar menu being gigantic.

let fontawesome = require('@fortawesome/fontawesome');
let solid       = require('@fortawesome/fontawesome-pro-solid').default;
let regular     = require('@fortawesome/fontawesome-pro-regular');
let light       = require('@fortawesome/fontawesome-pro-light');
let brands      = require('@fortawesome/fontawesome-free-brands');

fontawesome.library.add(solid);
fontawesome.library.add(regular);
fontawesome.library.add(light);
fontawesome.library.add(brands);
mix.js('resources/assets/js/app.js', 'public/js')
   .js('resources/assets/js/font-awesome.js', 'public/js')
   .sass('resources/assets/sass/app.scss', 'public/css');
Uncaught TypeError: Cannot read property 'icon' of undefined
    at font-awesome.js:1528
    at Array.reduce (<anonymous>)
    at define (font-awesome.js:1526)
    at font-awesome.js:1575
    at Array.forEach (<anonymous>)
    at Library.add (font-awesome.js:1573)
    at Object.defineProperty.value (font-awesome.js:89)
    at __webpack_require__ (font-awesome.js:20)
    at Object.<anonymous> (font-awesome.js:75)
    at __webpack_require__ (font-awesome.js:20)
tagliala commented 6 years ago

Hi!

Thanks for being part of the Font Awesome Community.

Sorry but I cannot help in your specific use case

Are you able to provide a reproducible test case?

defaye commented 6 years ago

Same issue with Laravel 5.5.34.

window.Vue = require('vue');

const fontawesome = require('@fortawesome/fontawesome');
const FontAwesomeIcon = require('@fortawesome/vue-fontawesome');
const fontawesomeProLight = require('@fortawesome/fontawesome-pro-light');
fontawesome.library.add(fontawesomeProLight);
Vue.component('font-awesome-icon', FontAwesomeIcon);

Error: Uncaught TypeError: Cannot read property 'icon' of undefined

function define(prefix, icons) {
  var normalized = Object.keys(icons).reduce(function (acc, iconName) {
    var icon = icons[iconName];
    var expanded = !!icon.icon;
defaye commented 6 years ago

index.es.js:

function define(prefix, icons) {
  console.log("pro-light");
  console.log(icons);
  var normalized = Object.keys(icons).reduce(function (acc, iconName) {
    var icon = icons[iconName];
    console.log(iconName, icon);
    var expanded = !!icon.icon;

console:

faWindowRestore {prefix: "fal", iconName: "window-restore", icon: Array(5)}
app.js?id=b36e112de10c78243a99:823 faWonSign {prefix: "fal", iconName: "won-sign", icon: Array(5)}
app.js?id=b36e112de10c78243a99:823 faWrench {prefix: "fal", iconName: "wrench", icon: Array(5)}
app.js?id=b36e112de10c78243a99:823 faYenSign {prefix: "fal", iconName: "yen-sign", icon: Array(5)}
app.js?id=b36e112de10c78243a99:3623 {} "undefined" undefined
defaye commented 6 years ago

I got beyond this issue by making the following changes:

function define(prefix, icons) {
  var normalized = Object.keys(icons).reduce(function (acc, iconName) {
    var icon = icons[iconName];
    if (icon) {
      var expanded = !!icon.icon;

      if (expanded) {
        acc[icon.iconName] = icon.icon;
      } else {
        acc[iconName] = icon;
      }
    }
    return acc;
  }, {});
  _byUnicode = lookup(function (acc, icon, iconName) {
    if (icon) {
      acc[icon[3]] = iconName;
    }

    return acc;
  });

  _byLigature = lookup(function (acc, icon, iconName) {
    if (icon) {
      var ligatures = icon[2];

      acc[iconName] = iconName;

      ligatures.forEach(function (ligature) {
        acc[ligature] = iconName;
      });
    }

    return acc;
  });

For some reason, I also got the following error:

app.js?id=4c1e65d490a5b4d7c41d:41164 [Vue warn]: Failed to mount component: template or render function not defined.

found in

---> <FontAwesomeIcon>

This was fixed by using ES6(?edit: said ES5 in error) import technique: (no idea why)

// const FontAwesomeIcon = require('@fortawesome/vue-fontawesome');
import FontAwesomeIcon from '@fortawesome/vue-fontawesome';

I also couldn't see subsequent icons using the following syntax:

<font-awesome-icon :icon="['fal', 'user']" />

I had to use:

                <font-awesome-icon :icon="['fal', 'user']"></font-awesome-icon>
                <font-awesome-icon :icon="['fab', 'facebook']"></font-awesome-icon>
bschwartz757 commented 6 years ago

Hmm, well if by "es5 import" you mean require (), that's not an es version thing- that's a CommonJS module (also used by Nodejs, but can be used in the browser as well.) If that solved your issue, then that may point to the problem: require is synchronous, while es6 import is not... however I was also experiencing a similar issue using require.

The solution for me was just to download the font bundle, move it into my compiled assets during gulp build, and then include it in the head tag of my pug template. Not the most modern/automated method but it does work.

Blake Schwartz

On Feb 15, 2018 3:42 PM, "Jonathan de Faye" notifications@github.com wrote:

I got beyond this issue by making the following changes:

function define(prefix, icons) { var normalized = Object.keys(icons).reduce(function (acc, iconName) { var icon = icons[iconName]; if (icon) { var expanded = !!icon.icon;

  if (expanded) {
    acc[icon.iconName] = icon.icon;
  } else {
    acc[iconName] = icon;
  }
}
return acc;

}, {});

_byUnicode = lookup(function (acc, icon, iconName) { if (icon) { acc[icon[3]] = iconName; }

return acc;

});

_byLigature = lookup(function (acc, icon, iconName) { if (icon) { var ligatures = icon[2];

  acc[iconName] = iconName;

  ligatures.forEach(function (ligature) {
    acc[ligature] = iconName;
  });
}

return acc;

});

For some reason, I also got the following error:

app.js?id=4c1e65d490a5b4d7c41d:41164 [Vue warn]: Failed to mount component: template or render function not defined.

found in

--->

This was fixed by using ES5(?) import technique: (no idea why)

// const FontAwesomeIcon = require('@fortawesome/vue-fontawesome'); import FontAwesomeIcon from '@fortawesome/vue-fontawesome';

I also couldn't see subsequent icons using the following syntax:

I had to use:

            <font-awesome-icon :icon="['fal', 'user']"></font-awesome-icon>
            <font-awesome-icon :icon="['fab', 'facebook']"></font-awesome-icon>

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/FortAwesome/Font-Awesome/issues/12278#issuecomment-366099593, or mute the thread https://github.com/notifications/unsubscribe-auth/AHK9nK5J1Sm_41as3kkDqprUOctBZwhPks5tVMDKgaJpZM4R9Uml .

Evanslooten commented 6 years ago

@tagliala I was able to reproduce this issue with a base install of Laravel. The steps I followed are listed below, please contact me directly and I can give you access to the Cloud9 test environment. 1) Create environment and upgrade to PHP 7.1 & MySQL 5.7. Install and configure Laravel 5.5. 2) Install Font Awesome via NPM 3) Create font-awesome.js (resources/admin/js/font-awesome.js)

let fontawesome = require('@fortawesome/fontawesome');
let solid       = require('@fortawesome/fontawesome-pro-solid').default;
let regular     = require('@fortawesome/fontawesome-pro-regular');
let light       = require('@fortawesome/fontawesome-pro-light');
let brands      = require('@fortawesome/fontawesome-free-brands');

fontawesome.library.add(solid);
fontawesome.library.add(regular);
fontawesome.library.add(light);
fontawesome.library.add(brands);

4) Add the created font-awesome.js to Mix (webpack.mix.js)

let mix = require('laravel-mix');

/*
 |--------------------------------------------------------------------------
 | Mix Asset Management
 |--------------------------------------------------------------------------
 |
 | Mix provides a clean, fluent API for defining some Webpack build steps
 | for your Laravel application. By default, we are compiling the Sass
 | file for the application as well as bundling up all the JS files.
 |
 */

mix.js('resources/assets/js/app.js', 'public/js')
   .js('resources/assets/js/font-awesome.js', 'public/js')
   .sass('resources/assets/sass/app.scss', 'public/css');

4) Reference the mix asset in view file (welcome.blade.php) <script src="{{ mix('js/font-awesome.js') }}"></script> 5) Run NPM to compile assets npm run dev 6) Error appears in console when viewing project

font-awesome.js:1526 Uncaught TypeError: Cannot read property 'icon' of undefined
    at font-awesome.js:1526
    at Array.reduce (<anonymous>)
    at define (font-awesome.js:1524)
    at font-awesome.js:1573
    at Array.forEach (<anonymous>)
    at Library.add (font-awesome.js:1571)
    at Object.45 (font-awesome.js:86)
    at __webpack_require__ (font-awesome.js:20)
    at Object.44 (font-awesome.js:71)
    at __webpack_require__ (font-awesome.js:20)
tagliala commented 6 years ago

@Evanslooten thanks.

I'm not so much into node.js.

I can just see that you are not using the recommended way to load font awesome:

import fontawesome from '@fortawesome/fontawesome'
import solid from '@fortawesome/fontawesome-free-solid'
import faTwitter from '@fortawesome/fontawesome-free-brands/faTwitter'

// Add entire styles or individual icons
fontawesome.library.add(solid, faTwitter)

I see require and .default in your approach, but I don't know the differences with import

@robmadole could you please take a look at this?

Evanslooten commented 6 years ago

@tagliala Changing font-awesome.js as follows does remove the console error. I still have issues with icons rendering strangely, but this is likely unrelated.

/*
Causes icon undefined error

let fontawesome = require('@fortawesome/fontawesome');
let solid       = require('@fortawesome/fontawesome-pro-solid').default;
let regular     = require('@fortawesome/fontawesome-pro-regular');
let light       = require('@fortawesome/fontawesome-pro-light');
let brands      = require('@fortawesome/fontawesome-free-brands');

fontawesome.library.add(solid);
fontawesome.library.add(regular);
fontawesome.library.add(light);
fontawesome.library.add(brands);
*/
/* Does not cause error */
import fontawesome from '@fortawesome/fontawesome'
import solid from '@fortawesome/fontawesome-pro-solid'
import regular from '@fortawesome/fontawesome-pro-regular'
import light from '@fortawesome/fontawesome-pro-light'
import brands from '@fortawesome/fontawesome-free-brands'

fontawesome.library.add(solid, regular, light, brands)
defaye commented 6 years ago

@Evanslooten are you telling me that if I just used import _ from _ I wouldn't have had this issue? require is in their docs! Sigh

robmadole commented 6 years ago

We missed the require issue in the docs. I'll get that fixed. Sorry @defaye.

robmadole commented 6 years ago

Ok, I actually think I can work around the issue with CommonJS so that this doesn't create headaches for people. I'll get this on the bug list.

robmadole commented 6 years ago

https://github.com/FortAwesome/Font-Awesome/issues/12436 https://github.com/FortAwesome/Font-Awesome/issues/12278 (branch started https://github.com/FortAwesome/fontawesome-buildsystem/tree/missing-icon-hijacking)

(Folks, Trello adds these and it's part of our internal tracking. Ignore em)

mlwilkerson commented 6 years ago

In Font Awesome 5.0.x, if you want to require the whole icon set, it should look like this:

const fas = require('@fortawesome/fontawesome-free-solid')['default']

(notice the ['default'] on the end, or it could be .default) If you look here at @fortawesome/fontawesome-free-solid/index.js, you'll see that we have an export on the default key that is just the icon set, and it's in the structure expected by library.add().

If you do the require() without the ['default'] at the end, you're getting the whole exports object which is not the expected structure for library.add().

Here's a working demo in a repo that require()s a whole icon set, adds it to the library and reads something back out.

That demo repo has two branches, because the way we export stuff is different in our current 5.0.x releases from what our 5.1.x branch will look like once released (currently in pre-release). So its master branch is for the 5.1 approach, and the 5.0-dependencies branch is for the for the 5.0.x approach (which I assume is what matters for the situations posted above).

@bschwartz757 I'm curious to know what difference it would make if you made two changes to what you originally posted:

  1. Add the ['default'] to your require().
  2. Call library.add() only once, passing in everything together as a comma separate list of args. Like this:
    var fontawesome = require('@fortawesome/fontawesome')
    var faBrands = require('@fortawesome/fontawesome-free-brands')['default']
    var faSolid = require('@fortawesome/fontawesome-free-solid')['default']
    var faRegular = require('@fortawesome/fontawesome-free-regular')['default']
    fontawesome.library.add(faBrands, faSolid, faRegular)
bschwartz757 commented 6 years ago

@mlwilkerson Thanks - that worked like a charm!

alexandrubau commented 6 years ago

Or you could require only the icons you need (not the entire pack) like this:

let fasEnvelope = require('@fortawesome/free-solid-svg-icons/faEnvelope').definition;
let icons = [
    fasEnvelope
];
fa.library.add(icons);
robmadole commented 6 years ago

@alexandrubau the add() method does not take an array argument.

Try:

const fasEnvelope = require('@fortawesome/free-solid-svg-icons/faEnvelope').definition;
fa.library.add(fasEnvelope);
abhijit-padhy commented 5 years ago

@tagliala @robmadole I am getting error only for 'faPencilAlt'

`import { faPencilAlt, faPlusCircle, ... } from '@fortawesome/free-solid-svg-icons'; library.add(faPencilAlt, ...)

`
tagliala commented 5 years ago

@abhijit-padhy sorry, cannot replicate: https://codesandbox.io/s/20wnlvl19j

Please open a new issue and fill out our bug report template