Eitan Peer
Eitan Peer

Reputation: 4345

Loading ng-src images with webpack

I am loading an image with angular, e.g.

<img ng-src="{{::path-to-image}}"/>

When I bundle my application with webpack the image URL is resolved in runtime, thus not bundled by webpack's loaders.

This is the image loader I am using:

  {
    test: /\.(jpe?g|png|gif|svg)$/i,
    loader: 'url?limit=8192!img'
  } 

How can webpack bundle those images resolved in runtime?

Upvotes: 12

Views: 8009

Answers (5)

dimson d
dimson d

Reputation: 1660

im just using CopyWebpackPlugin and no overhead at all

const CopyWebpackPlugin = require('copy-webpack-plugin');
// WriteFilePlugin  needed only for webpack 3-4 and webpack dev-server
const WriteFilePlugin = require('write-file-webpack-plugin');
plugins: [
  ...
  new WriteFilePlugin(),
  new CopyWebpackPlugin([
        {from: 'files', to: 'files'},
        {from: 'images-2x', to: 'images-2x'},
        {from: 'images', to: 'images'},
      ]),
]

Upvotes: 0

t.888
t.888

Reputation: 3902

I needed to dynamically resolve images at runtime with hashes generated by webpack, e.g., imgA.bd79a5ba.png.

{
  test: /\.png$/,
  loader: 'file-loader?name=images/[name].[hash].[ext]',
}

I ended up creating a module to alias and required the images:

let map = {
  'imgA': require('./images/imgA.png'),
  'imgB': require('./images/imgB.png'),
  'imgC': require('./images/imgC.png')
}

This produces the map:

{
  'imgA': imgA.bd79a5ba.png,
  'imgB': imgB.e51f66a2.png,
  'imgC': imgC.d84ae37c.png
}

In my component I resolved the url from the map:

self.link = function link(scope) {
  scope.imageUrl = function(name) {
    function return map[name];
  }
};

It was then trivial to load the image:

 <img ng-src="{{ imageUrl('imgA') }}">

The above is simplified. I found it useful to create a module for the map:

export default {
  'imgA': require('./images/imgA.png'),
  'imgB': require('./images/imgB.png'),
  'imgC': require('./images/imgC.png')
}

then import it into the component module:

import urlMap from './imageUrlMap'
console.log(urlMap.imgA)

> imgA.bd79a5ba.png

Upvotes: 0

Lakatos Gyula
Lakatos Gyula

Reputation: 4160

Because I also needed thus functionality and found the original answer was far from a perfect solution I ended up figuring it out on my own.

Write a function in the controller:

$scope.loadImage = function(image) {
    return require('/images/' + image);
};

And use it in your ng-src:

<img ng-src="{{loadImage('myImage')}}" />

After that to make dynamic requires work you can use a context.

For example: https://github.com/webpack/webpack/tree/master/examples/require.context#examplejs

Upvotes: 18

cusejuice
cusejuice

Reputation: 10691

I created a uiImage directive which requires the images.

 function uiImage() {
    return {
      restrict: 'A',
      link: function(scope, element, attr) {
        var src = attr.uiImage || attr.src;

        function loadImage(image) {
          return require(`Images/${image}`);
        }

        // required for webpack to pick up image
        element.attr('src', loadImage(src));
      }
    };
  }

  uiImage.$inject = [];
  export default uiImage;

Usage:

<img ui-image='myimage.png' />

webpack.config.js

I have a Webpack resolve Images which points to the location of all my images.

resolve: {
  alias: {
   'Images': path.join(config.appDir, '/images')
  }
}

Upvotes: 2

gonzoyumo
gonzoyumo

Reputation: 349

If you need an image path that is programmatically generated, it means you have some logic expecting this image to exist. In other words, you should consider this image as a dependency of that piece of logic, thus you need to require it explicitely.

In your JS code (eg: controller)

this.imageUrl = require('path-to-image' + someDynamicValue + '.jpg');

In your template:

<img ng-src="{{::myCtrl.imageUrl}}"/>

Webpack is smart enough to understand the dynamic require statement and bundle your images that will match that expression. Check the documentation for more details: https://webpack.github.io/docs/context.html)

Upvotes: 10

Related Questions