Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically loading assets with require() that might not exist in webpack + VueJS

I'm trying to load images dynamically which may or may not exist.

In this case, crypto-currency icons using their 3 letter symbol. I have a few hundred in .svg format in my statics library and when I pull data from a price server I try to match up the icons I have with the symbols coming from the server and to serve a fallback image if I don't have the asset.

In my index.vue I can get away with this code and everything works fine:

<img :src="'statics/icons/svg/' + coin.symbol + '.svg'" v-img-fallback="'statics/icons/svg/fallback.svg'"/>

However in a subcomponent that opens if a user clicks a coin the same code will fail to load both the primary and fallback images. I've tried numerous ways but the only way to get an image to load from my subcomponent is to either hard code it like this:

<img src="statics/icons/svg/btc.svg"/>

Which is impossible for me as I need the modal to be dynamically generated for any possible coin...

Or using require() like this:

<img :src="imageSrc" v-img-fallback="require('../statics/icons/svg/fallback.svg')"/>
// Computed:
imageSrc () {
  if (this.coinData.symbol) {
     return require('../statics/icons/svg/' + this.coinData.symbol + '.svg')
  }
}

However this crashes my app if require() looks for an asset that doesn't exist. I need a method that fails gracefully so that the v-img-fallback can detect it and supply the fallback.

I've tried doing something like return require(image1) || require(fallback) but it doesn't work.

like image 609
altShiftDev Avatar asked Apr 04 '18 19:04

altShiftDev


1 Answers

This is a common request and latest WebPack, AFAIK (and I just searched for it again), does not expose an API for especifically testing the existence of a module.

In other words, you'd have to handle the uncertainty of the loading yourself. Example:

  computed: {
    imageSrc () {
      if (this.coinData.symbol) {
        try {
          return require('../statics/icons/svg/' + this.coinData.symbol + '.svg')
        } catch (e) {
          if (e.name !== "ModuleNotFoundError") throw e; // handle false-positives
          // in cordova, use the line below instead of the above
          // if (!e.message.startsWith('Cannot find module')) throw e;
          return require('../statics/icons/svg/fallback.svg');
        }
      }
      return require('../statics/icons/svg/fallback.svg');
    }
  }

This way I'd argue you wouldn't even need a fallback src in the template. You could return it in the computed property itself.

like image 72
acdcjunior Avatar answered Oct 28 '22 14:10

acdcjunior