0

I can't seem to find an answer for this.

I can easily route all 404s intended for a page route to another route with my Routes module, with a derivative of this. Got it.

{ path: '**', component: PageNotFoundComponent }

I have two questions:

1) How can I redirect to a static file that does not need to be a component of my project?

2) How can I do this for an individual file?

Forgive me if my terminology is not exactly correct. I have only just started using Angular.

Basically, what I want to do is redirect a 404 error for an image within a particular folder to a known image within the same folder. The idea, using the above example, would be something like this:

{ path: '/assets/custom-images/**', redirectTo: '/assets/custom-images/goodimage.png' }

In .NET I can do this very easily at the IIS level by adding a simple block to a web.config within the 'custom-images' folder, like this:

<httpErrors errorMode="Custom">
  <remove statusCode="404"/>
  <error statusCode="404" path="default.png" responseMode="Redirect"/>
</httpErrors>

And this actually works just fine, when I deploy my project to my IIS production site, but it does not work while I am working in my node.js development environment running ng serve on port :4200

Again, forgive me if I am not using the perfect terminology, but I hope someone gets my point here.

iGanja
  • 2,374
  • 2
  • 28
  • 32

1 Answers1

0

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!

Neil VanLandingham
  • 1,016
  • 8
  • 15
  • Okay, I am excited by the possibility, but I am having difficulty figuring out how to insert this solution into my project. I am using TypeScript and Angular, and this looks more like JQuery and AngularJS. So how do I import this into my routing module? – iGanja Dec 25 '19 at 22:34
  • Ah, sorry. The first solution I posted was a server-side solution for Node/Express. I have updated my answer to include three different approaches to solving this problem with client-side code, but you may still want to consider implementing the server-side solution for reasons I explain in the post – Neil VanLandingham Dec 26 '19 at 06:39
  • As I stated in my question, the server-side approach you mention works just fine when I deploy to an IIS server running .NET. I have no desire to install or publish on a node server in production. I ended up using your old school declarative example, as I only really need this to work during development. I am still wondering how I can configure my development node server environment to do your server-side solution. I may play with this later. Thanks! – iGanja Dec 26 '19 at 16:15