35

The johnpapa Angular 2 style guide suggests a folder-by-feature approach. I get the idea, you can make self contained little angular components that can be reused.

So, I made a component I wanted to reuse in another project and put it in it's own folder. I also added an image I wanted this component to display to the same folder, so it's all self contained.

<img class="logo" src="logo.png"/>

But this tries to then load the image from root localhost:3000/logo.png.

I suppose this means I have to actually use the exact path to the image, but doesn't this undermine the whole idea of components that can be reused in other project by other people?

Suggestions on this?

Edit for clarification I am using the folder structure from the Angular 2 quickstart, meaning my root folder is:

app/
node_modules/
index.html
package.json
tsconfig.json

So, even if I use the path header/logo.png, it does not work. I have to do app/header/logo.png. This is effectively an absolute path, and in fact works equally as well if I add a leading slash: "/app/header/logo.png". Anything less than the full path breaks the link. Meaning if someone wanted to reuse this they would have to have the exact same folder structure.

I guess this is just how it works, I'm only just learning Angular 2, but in my mind I should be able to load assets from within a components folder just like I can with the template or css

Rob Louie
  • 2,462
  • 1
  • 19
  • 24
  • 1
    Do absolute path so you can use it everywhere? – Yann Chabot Mar 29 '16 at 14:50
  • 1
    just add the minimal path to require to get the image, so if it's under: component1/images/logo.png, write: – Tomer Mar 29 '16 at 14:59
  • 1
    I have edited my question for clarification. It's about how to keep modules self contained within their folder without worrying about where that folder lives within the app. That way it can be reused easily in other projects. However, it seems that's just now how it's supposed to work. – Rob Louie Mar 29 '16 at 17:00
  • Hi Rob, Did you ever resolve this? – David Blaney Jan 05 '17 at 17:13
  • This answer helps me, its should work for you too
    https://stackoverflow.com/a/42660412/4788476
    – chavy Oct 27 '17 at 09:16

7 Answers7

41

I am using webpack and have been able to overcome this issue by requireing the image in the component as a variable and using this in my template.

This is because during the bundling phase webpack will load the resource and store the correct URL and calling require at runtime will get this correct URL to pass to the template.


Example

Using the following directory structure

app/
    header/
        header.component.ts
        header.component.html
        assets/
            logo.png
...

header.component.ts

...
@Component({
    selector: 'header',
    templateUrl: './header.component.html', // Auto required by webpack
})
export class HeaderComponent {
    private LOGO = require("./assets/logo.png");

    constructor() {};
}

header.component.html

<div>
    <img [src]="LOGO" />
</div>

Admittedly this binds the component and template but the require needs to be in the component so that webpack is able to analyse and load it when bundling.


With this approach I have packaged my module using npm and installed and used it in another project - which also uses webpack.

I am yet to test with SystemJS.

David Blaney
  • 898
  • 8
  • 17
  • This is very interesting, thanks for posting this. Back when I originally asked this question (almost a year ago now!), I hadn't gotten too deep into webpack. This feels to me like a really good solution. I'm going to try this out tonight. – Rob Louie Jan 11 '17 at 18:31
  • It works also with absolute paths and webpack alias!!! This is the best answer, and i think the best way to import images because you don't have to write a relative paths with a lot of ugly: src=".:/../../../../../finally-my-image.png". – raythurnevoid May 25 '17 at 20:49
  • 1
    admittedly this is a perfect solution considering it takes care of webpack bundling and even for accessing images from node_modules this solution is perfect, usage: `private logo = require('3rdPartyNodeModules/src/images/test.svg')` – akhouri Dec 06 '17 at 12:36
  • 1
    If I recall correctly it works fine in an AOT project. The change I had to make was to remove the private modifier as the ng compiler would not work with that. The angular cli uses Webpack under the hood and so the same require principles are applied here. – David Blaney Jan 16 '18 at 07:40
  • Man. You saved the day. I was trying with 50 different methods including `ControlValueAccessor`, but this one works perfectly. – mutantkeyboard Apr 04 '18 at 07:33
  • I get an error here `http://localhost:4200/[object%20Module]`. I think it has to do with that is does not load well. My core version is `~9.1.6` – Nick Alexander May 11 '20 at 00:15
  • To get rid of the [object%20Module] error use: `private LOGO = require("./assets/logo.png").default;` When you upgraded Angular to 9 the file-loader also got upgraded from 4.2 to 6. I had the same issue. Adding **.default** solved my issue. For more info look at this answer: https://stackoverflow.com/a/59075858 – Gennadiy Jun 18 '20 at 21:18
3

I would solve this problem by splitting the src path into 2 parts, like this:

<img class="logo" [src]="(imgPath + imgFileName)" />

then, inside the component definition, you set:

@Input() imgPath:string = "app/header/";
imgFileName:string = "logo.png";

By using the @Input() decorator, the imgPath variable can be seen externally, so that in case you move the component into another place you can set a different path, making the component reusable.

leonardomerlin
  • 120
  • 1
  • 7
rekotc
  • 595
  • 1
  • 10
  • 21
3

@David's solution is slightly outdated given that now angular2 can be upgrade, it works for anular 2/3. For angular 4 and above it would throw require not found error, this is how i modified it,

header.component.ts

...
declare var require: any;

@Component({
    selector: 'header',
    templateUrl: './header.component.html', // Auto required by webpack
})
export class HeaderComponent {
    private LOGO = require("./assets/logo.png");

    constructor() {};
}

header.component.html

<div>
    <img [src]="LOGO" />
</div>
ishandutta2007
  • 16,676
  • 16
  • 93
  • 129
0

Use an assets folder in your root that contains the logo image

Mackelito
  • 4,213
  • 5
  • 39
  • 78
0

If the problem is a 404 error you need to change your path

from path/image (root/path/image, /path/image etc.)

to ./path/image

Then it should work if there aren't any other problems.

ivarni
  • 17,658
  • 17
  • 76
  • 92
0

Check out this answer: how to serve up images in angular2

The key is to either put it into the "assets" folder or to edit your ".angular-cli.json" file (assets section) to include the location where the image is located. This is also true for other resources that are supposed to be served up, i.e. style sheets.

Papa Stahl
  • 687
  • 8
  • 7
0

You can store the images under the assets folder and refer to them thus:

Do not use a relative path from the component.

See also: https://mdbootstrap.com/support/angular/images-not-loading-from-assets-folder-in-angular/

  • While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - [From Review](/review/late-answers/34751336) – ToDevAndBeyond Aug 02 '23 at 18:29