Angular — Fallback for Broken Images

If you have worked with images on websites then you must have run into the common problem where the image doesn’t load for some reason. Maybe the link is broken, or the link might be an empty string/null/undefined. The image fails to load and you see something like this:

Images failed to loadImages failed to load

This is a very unpleasant sight for the user. In order to work around this in Angular 2+, you have a variety of solutions. The solution I prefer is to create a directive for the elements on the page. If the image fails to load I show a default image that i pass in the element as a parameter. So i can show different default images in different parts of my website if I need to.

Note that this is for Angular 2+. Directives in Angular 1.x.x are entirely different. In general, directives are used to change the default behavior of an element in Angular 2+.

So coming back to the subject, I created an ImagePreload Directive:

import {Directive, Input, HostBinding} from '@angular/core'

      @Directive({
        selector: 'img[default]',
        host: {
          '(error)':'updateUrl()',
          '(load)': 'load()',
          '[src]':'src'
         }
      })
      
     export class ImagePreloadDirective {
        @Input() src:string;
        @Input() default:string;
        @HostBinding('class') className
      
        updateUrl() {
          this.src = this.default;
        }
        load(){
          this.className = 'image-loaded';
        }
      }
  

In the decorator, I have a selector property and a host property. In the selector property I tell the Directive to apply on every element that matches the css selector img[default]. I write [default] here because I will give an attribute default to the element next.

The host property gives me access to the default attributes on this element such as error, load and src. I bind them to my methods and properties on my ImagePreloadDirective class. So it means that when the default error event on the element is invoked, my updateUrl() method will run too. Same goes for the load event. The [src] is not an event. Its an attribute on every element. Here I’m just saying that my src property in my class should be bound to the element’s src property. So if I change my property, it should change the element’s property. It might sound confusing but it’s extremely simple when you get the hang of it.

Moving on into the class, we have an 2 inputs to the directive src and default . The third one is a HostBinding. It lets you set properties on the element or component that hosts the directive, in this case img[default] .

Our element in the html will now look like this:

<img 
      src="test.com/img-1" 
      default="test.com/default-img" 
      class="img"
    >

So let’s say that the url test.com/img-1 is broken for some reason, the error event on the element will fire. This in turn will fire our updateURL() method that will set the src attribute of the element to the default url that we provided to the element. In the event that the url test.com/img-1 loads fine, the load event on the element will fire. It will fire our load() event and it will add a class ‘image-loaded’ to the element. You can use this class to show or hide loading-spinners/image-loading-overlays.

So after you use this method, your website will look like something like this:

This is all you need to setup default image loading on your images. I like this approach because its short and simple. You can extend the functionality in the directive if you need. It only affects the elements you want and not all the image elements. It also allows you to show different fallback images for different elements.

Hope this short article helped you improving the look of your website. Cheers!

2019-05-14