22

I am not able to import images in my headercomponent.ts. I suspect it is because of something i am doing wrong while compiling ts(using webpack ts loader) because same thing works with react( where the components are written in es6)

The error location is

//headercomponent.ts
import {Component, View} from "angular2/core";
import {ROUTER_DIRECTIVES, Router} from "angular2/router";
import {AuthService} from "../../services/auth/auth.service";
import logoSource from "../../images/logo.png"; //**THIS CAUSES ERROR**  Cannot find module '../../images/logo.png'

@Component({
    selector: 'my-header',
    //templateUrl:'components/header/header.tmpl.html' ,
    template: `<header class="main-header">
  <div class="top-bar">
    <div class="top-bar-title">
      <a href="/"><img src="{{logoSource}}"></a>
    </div>

my webpack config is

// webpack.config.js
'use strict';

var path = require('path');
var autoprefixer = require('autoprefixer');
var webpack = require('webpack');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var basePath = path.join(__dirname,'public');
//const TARGET = process.env.npm_lifecycle_event;
console.log("bp " + basePath)
module.exports = {
  entry: path.join(basePath,'/components/boot/boot.ts'),
  output: {
    path: path.join(basePath,"..","/build"), // This is where images AND js will go
    publicPath: path.join(basePath,"..","/build/assets"),
   // publicPath: path.join(basePath ,'/images'), // This is used to generate URLs to e.g. images
    filename: 'bundle.js'
  },
  plugins: [
    new ExtractTextPlugin("bundle.css")
  ],
  module: {
    preLoaders: [ { test: /\.tsx$/, loader: "tslint" } ],
    //
    loaders: [
      { test: /\.(png!jpg)$/, loader: 'file-loader?name=/img/[name].[ext]'  }, // inline base64 for <=8k images, direct URLs for the rest
      {
        test: /\.json/,
        loader: 'json-loader',
      },
      {
        test: /\.ts$/,
        loader: 'ts-loader',
        exclude: [/node_modules/]
      },
      {
        test: /\.js$/,
        loader: 'babel-loader'
      },
      {
        test: /\.scss$/,
        exclude: [/node_modules/],
        loader: ExtractTextPlugin.extract("style", "css!postcss!sass?outputStyle=expanded")
      },
      // fonts and svg
      { test: /\.woff(\?v=\d+\.\d+\.\d+)?$/, loader: "url-loader?limit=10000&mimetype=application/font-woff" },
      { test: /\.woff2(\?v=\d+\.\d+\.\d+)?$/, loader: "url-loader?limit=10000&mimetype=application/font-woff" },
      { test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, loader: "url-loader?limit=10000&mimetype=application/octet-stream" },
      { test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, loader: "file" },
      { test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, loader: "url-loader?limit=10000&mimetype=image/svg+xml" }
    ]
  },
  resolve: {
    // now require('file') instead of require('file.coffee')
    extensions: ['', '.ts', '.webpack.js', '.web.js', '.js', '.json', 'es6', 'png']
  },
  devtool: 'source-map'
};

and my directory structure looks like this

-/
 -server/
 -build/
 -node-modules/
 -public/
  -components/
   -boot/
    -boot.component.ts
   -header/
    -header.component.ts
  -images/
   -logo.png
  -services/
-typings/
 -browser/
 -main/
 -browser.d.ts
 -main.d.ts
-tsconfig.json
-typings.json

my tsconfig file is as follows:

 //tsconfig.json
     {
      "compilerOptions": {
        "target": "es5",
        "sourceMap": true,
        "emitDecoratorMetadata": true,
        "experimentalDecorators": true,
        "removeComments": false,
        "noImplicitAny": false
      },
      "exclude": [
        "node_modules"
      ]
    }

I suspect I am messing some thing in typescript compilation , not sure what

Sahil Sharma
  • 1,493
  • 2
  • 10
  • 22
  • You can import whatever you want in typescript now, but it's not totally apparent. See this issue to use `# declarations.d.ts declare module '*'; # index.ts import * as $ from 'jquery'; import * as _ from 'lodash';` https://github.com/Microsoft/TypeScript/issues/2709 – William S Sep 10 '16 at 10:16

7 Answers7

44

The problem is that you confuse TypeScript level modules and Webpack level modules.

In Webpack any file that you import goes through some build pipeline.

In Typescript only .ts and .js files are relevant and if you try to import x from file.png TypeScript just does not know what to do with it, Webpack config is not used by TypeScript.

In your case you need to separate the concerns, use import from for TypeScript/EcmaScript code and use require for Webpack specifics.

You would need to make TypeScript ignore this special Webpack require syntax with a definition like this in a .d.ts file:

declare function require(string): string;

This will make TypeScript ignore the require statements and Webpack will be able to process it in the build pipeline.

Egel
  • 1,796
  • 2
  • 24
  • 35
bestander
  • 661
  • 5
  • 5
  • Thanks, this did solve the current problem. But still there still are so many battles to be won. I hope this gets better when angular2 becomes stable. I wish there was an official starter kit / workflow guidelines. It feels like they themselves are still unsure of many things. – Sahil Sharma Mar 22 '16 at 13:50
  • This is not entirely the case anymore, you can import whatever you want somewhat easily now using `declare module "*";`. https://github.com/Microsoft/TypeScript/issues/2709 – William S Sep 10 '16 at 10:18
  • Hey, i getting problem when implementing this with React. Take a look here: https://stackoverflow.com/questions/51100401/typescript-image-import – Mario Petrovic Jun 29 '18 at 11:12
22

Instead of:

import image from 'pathToImage/image.extension';

Use:

const image = require('pathToImage/image.extension');
zeh
  • 10,130
  • 3
  • 38
  • 56
nadav
  • 552
  • 5
  • 11
  • 3
    this worked, it should be the correct answer. However, it's like black magic to me. I don't understand how something so basic should be so un-intuitive. How did you know this? – SpaceMonkey Jan 05 '18 at 01:50
  • now I don't know how to actually use it.. If I log it, I see a URL which opens the image which is great, but if I try to use it as a source for an "img" tag, it seems to be reading it as a very long string that looks more like the content of the image file (svg). This is extremely confusing, but thanks for your help – SpaceMonkey Jan 05 '18 at 01:59
  • Best solution by far and in my opinion the best practice and way to go as for readability and usability. You dont have to declare modules or hack tsc or webpack. – Florian Leitgeb Feb 21 '18 at 14:20
12

I'm using

import * as myImage from 'path/of/my/image.png';

and created a typescript definition with

declare module "*.png" {
    const value: any;
    export = value;
}

This only works when you have a correct handler like the file-loader in webpack. Because this handler will give you a path to your file.

  • 1
    The key for me was to create the typescript definition file. After that, Typescript allows the import. – miracle2k Jun 23 '17 at 23:10
  • 1
    The problem is when you build in production, every static images should be add `md5` suffix. So, your `import * as myImage from 'path/of/my/image'` broken. Another problem is some small size images should be used by `base64` url, `url-loader` can convert the small size image to `base64` url. with `react`, all thing work fine. But `angular2`, I am still looking for a way to solve my problems. – Lin Du Aug 16 '17 at 05:54
5

A small improvement to Christian Stornowski's answer would be to make the export default, i.e.

declare module "*.png" {
  const value: string;
  export default value;
}

So you can import an image using:

import myImg from 'img/myImg.png';
Sunil Garg
  • 14,608
  • 25
  • 132
  • 189
Erik Vullings
  • 5,550
  • 2
  • 28
  • 23
3

I also had same issue so I used following approach:

import * as myImage from 'path/of/my/image';

In my component I simply assigned the imported image to a data member;

export class TempComponent{
    public tempImage = myImage;
}

and used it in template as:

<img [src]="tempImage" alt="blah blah blah">
Hitesh Kumar
  • 3,508
  • 7
  • 40
  • 71
  • The problem is when you build in production, every static images should be add `md5` suffix. So, your `import * as myImage from 'path/of/my/image'` broken. – Lin Du Aug 16 '17 at 05:49
2

If you want to use the ES6 syntax for importing.

First be sure that in your tsconfig.json you have:

target: 'es5',
module: 'es6'

The following should now work:

import MyImage from './images/my-image.png';
Vedran
  • 10,369
  • 5
  • 50
  • 57
  • Great! It was the solution for me. I am just wondering when I will reach the first limitation when 'common.js' import will be required... – Darkowic Apr 04 '19 at 18:52
0

To be able to use default import like this:

import grumpyCat from '../assets/grumpy_cat.jpg';

Define jpg module declaration:

declare module "*.jpg" {
const value: string;
  export default value;
}

and in your tsconfig use "module": "es6" (see @vedran comment above) or when you use "module": "commonjs" add "esModuleInterop": true,

  "compilerOptions": {
    "module": "commonjs",
    "target": "es5",
    "esModuleInterop": true,
...

Source: https://github.com/TypeStrong/ts-loader/issues/344#issuecomment-381398818

Darkowic
  • 661
  • 8
  • 17