All files / app/directives/imgLoad img-load.directive.ts

100% Statements 13/13
100% Branches 4/4
100% Functions 6/6
100% Lines 13/13

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108                                                          1x   328x               328x                 328x 328x                   322x 320x 320x                                 51x                       2x               52x 51x 51x              
import {
  Directive,
  ElementRef,
  HostListener,
  Input,
  OnChanges,
  SimpleChanges,
} from '@angular/core';
import { ImageService } from 'src/app/services/image/image.service';
import { Preloaders } from 'src/app/services/preloader/preloaders/preloaders';
 
/**
 * Directive used to indicate that the app should track the loading of the
 * images the directive is applied to, and tie their loading to a set of
 * preloaders given as input.
 *
 * Inspired by https://dev.to/paviad/angular-wait-for-all-images-to-load-3hp1
 *
 * For instance, using
 *
 * @example
 *   <img [appImgLoad]="myPreloaderArray" ...\>
 *
 *   will result in having every preloaders in `myPreloaderArray` take into account the loading of the image. See {@link Preloaders} for more info.
 */
@Directive({
  selector: 'img[appImgLoad]',
  standalone: true,
})
export class ImgLoadDirective implements OnChanges {
  /** The preloaders used for the image having the directive. */
  @Input() appImgLoad: Preloaders[] = [];
  /**
   * The notification to the preloaders that the image is loading is tied to the
   * injection of a value into the {@link appImgLoad} input. Since that value
   * could be binded and changed at runtime, this boolean tracks whether or not
   * it's the first binding of the value, and more practically if the img
   * already has loaded (or not in case of an error)
   */
  isLoadedOrError = false;
 
  /**
   * Image loading directive constructor
   *
   * @param elementRef The `ElementRef`
   * @param imageService The {@link ImageService}
   */
  constructor(
    private elementRef: ElementRef,
    private imageService: ImageService
  ) {}
 
  /**
   * When the directive value is injected, and the array of preloader is ready,
   * notify the {@link ImageService} that the image is now loading.
   *
   * @param changes The `SimpleChanges`
   */
  ngOnChanges(changes: SimpleChanges) {
    if (changes['appImgLoad'] && !this.isLoadedOrError) {
      setTimeout(() => {
        this.imageService.imageLoading(
          this.elementRef.nativeElement,
          this.appImgLoad
        );
      }, 0);
    }
  }
 
  /**
   * When the image is loaded, the {@link ImageService} should be notified. In
   * our case, it makes no difference if its actually loaded or if there was an
   * error. In both cases, the ressources fetching process is finished. The
   * function {@link loadOrError} makes the actual call to the
   * {@linkImageService}.
   */
  @HostListener('load')
  onLoad() {
    this.loadOrError();
  }
 
  /**
   * When the image is not loaded due to an error, the {@link ImageService}
   * should be notified. In our case, it makes no difference if its actually
   * loaded or if there was an error. In both cases, the ressources fetching
   * process is finished. The function {@link loadOrError} makes the actual call
   * to the {@linkImageService}.
   */
  @HostListener('error')
  onError() {
    this.loadOrError();
  }
 
  /**
   * Makes the actual call to the {@linkImageService} in case the image is
   * either loaded or if there was an error.
   */
  loadOrError() {
    if (!this.isLoadedOrError) {
      this.isLoadedOrError = true;
      this.imageService.imageLoadedOrError(
        this.elementRef.nativeElement,
        this.appImgLoad
      );
    }
  }
}