ionic-image-loader
Version:
Ionic Component and Service to load images in a background thread and cache them for later use
230 lines (181 loc) • 6.19 kB
text/typescript
import { Component, Input, Output, ElementRef, Renderer, OnInit, EventEmitter } from '@angular/core';
import { ImageLoader } from '../providers/image-loader';
import { ImageLoaderConfig } from '../providers/image-loader-config';
export class ImgLoader implements OnInit {
/**
* The URL of the image to load.
*/
set src(imageUrl: string) {
this._src = this.processImageUrl(imageUrl);
this.updateImage(this._src);
};
get src(): string {
return this._src;
}
private _src: string;
/**
* Fallback URL to load when the image url fails to load or does not exist.
*/
fallbackUrl: string = this._config.fallbackUrl;
/**
* Whether to show a spinner while the image loads
*/
spinner: boolean = this._config.spinnerEnabled;
/**
* Whether to show the fallback image instead of a spinner while the image loads
*/
fallbackAsPlaceholder: boolean = this._config.fallbackAsPlaceholder;
/**
* Use <img> tag
*/
set useImg(val: boolean) {
this._useImg = val !== false;
}
private _useImg: boolean = this._config.useImg;
/**
* Convenience attribute to disable caching
* @param val
*/
set noCache(val: boolean) {
this.cache = val !== false;
}
/**
* Enable/Disable caching
* @type {boolean}
*/
cache: boolean = true;
/**
* Width of the image. This will be ignored if using useImg.
*/
width: string = this._config.width;
/**
* Height of the image. This will be ignored if using useImg.
*/
height: string = this._config.height;
/**
* Display type of the image. This will be ignored if using useImg.
*/
display: string = this._config.display;
/**
* Background size. This will be ignored if using useImg.
*/
backgroundSize: string = this._config.backgroundSize;
/**
* Background repeat. This will be ignored if using useImg.
*/
backgroundRepeat: string = this._config.backgroundRepeat;
/**
* Name of the spinner
*/
spinnerName: string = this._config.spinnerName;
/**
* Color of the spinner
*/
spinnerColor: string = this._config.spinnerColor;
/**
* Notify on image load..
*/
load: EventEmitter<ImgLoader> = new EventEmitter<ImgLoader>();
/**
* Indicates if the image is still loading
* @type {boolean}
*/
isLoading: boolean = true;
element: HTMLElement;
constructor(
private _element: ElementRef,
private _renderer: Renderer,
private _imageLoader: ImageLoader,
private _config: ImageLoaderConfig
) {}
ngOnInit(): void {
if (this.fallbackAsPlaceholder && this.fallbackUrl) {
this.setImage(this.fallbackUrl, false);
}
if (!this.src) {
// image url was not passed
// this can happen when [src] is set to a variable that turned out to be undefined
// one example could be a list of users with their profile pictures
// in this case, it would be useful to use the fallback image instead
// if fallbackUrl was used as placeholder we do not need to set it again
if (!this.fallbackAsPlaceholder && this.fallbackUrl) {
// we're not going to cache the fallback image since it should be locally saved
this.setImage(this.fallbackUrl);
} else {
this.isLoading = false;
}
}
}
private updateImage(imageUrl: string) {
this._imageLoader.getImagePath(imageUrl)
.then((imageUrl: string) => this.setImage(imageUrl))
.catch((error: any) => this.setImage(this.fallbackUrl || imageUrl));
}
/**
* Gets the image URL to be loaded and disables caching if necessary
* @returns {string}
*/
private processImageUrl(imageUrl: string): string {
if (this.cache === false) {
// need to disable caching
if (imageUrl.indexOf('?') === -1) { // add ? if doesn't exists
imageUrl += '?';
}
if (['&', '?'].indexOf(imageUrl.charAt(imageUrl.length)) === -1) { // add & if necessary
imageUrl += '&';
}
// append timestamp at the end to make URL unique
imageUrl += 'cache_buster=' + Date.now();
}
return imageUrl;
}
/**
* Set the image to be displayed
* @param imageUrl {string} image src
* @param stopLoading {boolean} set to true to mark the image as loaded
*/
private setImage(imageUrl: string, stopLoading: boolean = true): void {
this.isLoading = !stopLoading;
if (this._useImg) {
// Using <img> tag
if (!this.element) {
// create img element if we dont have one
this.element = this._renderer.createElement(this._element.nativeElement, 'img');
}
// set it's src
this._renderer.setElementAttribute(this.element, 'src', imageUrl);
if (this.fallbackUrl && !this._imageLoader.nativeAvailable) {
this._renderer.setElementAttribute(this.element, 'onerror', `this.src="${ this.fallbackUrl }"`);
}
} else {
// Not using <img> tag
this.element = this._element.nativeElement;
if (this.display) {
this._renderer.setElementStyle(this.element, 'display', this.display);
}
if (this.height) {
this._renderer.setElementStyle(this.element, 'height', this.height);
}
if (this.width) {
this._renderer.setElementStyle(this.element, 'width', this.width);
}
if (this.backgroundSize) {
this._renderer.setElementStyle(this.element, 'background-size', this.backgroundSize);
}
if (this.backgroundRepeat) {
this._renderer.setElementStyle(this.element, 'background-repeat', this.backgroundRepeat);
}
this._renderer.setElementStyle(this.element, 'background-image', 'url(\'' + ( imageUrl || this.fallbackUrl ) + '\')');
}
this.load.emit(this);
}
}