Since you are dealing with a static image in this case and not an Angular component, it makes sense to me to implement the redirect within the server side logic instead of in the client side, especially since most network requests for images on the client side are handled automatically via the img
element and it's src
attribute. Or what if you have multiple clients that need to fetch images from your server? Don't you want all of the clients to get the fallback image? Maybe not... At any rate, I describe three different approaches to handling image request 404s on the client-side below, as well as one server-side approach using Node and Express.
The Easy Client-Side Approach
The Declarative Version
<img src="foo.jpg" onerror="if (this.src != 'fallback.jpg') this.src = 'fallback.jpg';">
We use the onerror
callback built into the native img
element to detect the 404, and we switch the value of the src
attribute to the path of the fallback image so that browser will automatically handle loading, decoding, and rendering the image.
Source: Inputting a default image in case the src attribute of an html <img> is not valid?
The Imperative Version
You can use this same approach imperatively. Inside your parent component, the one that contains all the images, use the ElementRef
class provided by Angular to query the img
elements and set their onerror callback.
import { Component, ElementRef } from '@angular/core';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
constructor(private elRef:ElementRef) {}
ngAfterViewInit() {
var imgs = this.elRef.nativeElement.querySelectorAll('img');
imgs.forEach(img=>{
img.onerror = switchToFallback;
})
}
}
function switchToFallback(){
this.src = "https://cdn.shopify.com/s/files/1/0533/2089/files/placeholder-images-image_large.png?v=1530129081"
}
The Needlessly Complicated Angular Client-Side Approach
On this apporach, we create a custom image component to use in lieu of native img
elements in our templates. First, you would create an imagerequest.service.ts
in your Angular code, and also a my-custom-image
component.
The image request service would use Angular's HttpClient
to attempt to fetch the image based on what we could call the imgScr
property of our my-custom-image
component. If the response code is 404, the image request service would then request and receive the fallback image prior to piping the response data to an observable. (I'm assuming you are using RxJS).
Then, inside the my-custom-image
component that consumes the image request service, you would use the Filereader API to create a blob from the response that you can use as a dataurl inside the actual img
element rendered by your my-custom-image
template.
The Server-Side Approach with Node/Express
To avoid having to check the filesystem yourself to see if the image exists, you could use express.static
to serve your image files. By default, the fallthrough
option of express.static
is TRUE, and that means that if no static file matches the requested route, express will then execute the next middleware function.
At the end of your routing endpoints, add a middleware function as a fallback to handle 404s. All requests that are not matched by some other routing endpoint will match this one.
// 404
app.use(function(req, res, next) {
return res.status(404).send({ message: 'Route'+req.url+' Not found.' });
});
Then inside your 404 fallthrough function, check the requested route, and if it matches your definition, then redirect to the fallback image. Something like this:
// 404
app.use(function(req, res, next) {
if(req.path.includes("/assets/custom-images/")){
res.redirect("/assets/custom-images/fallback.jpg");
}else{
return res.status(404).send({ message: 'Route'+req.url+' Not found.' });
}
});
Hope this helps!