Ionic 2

Caching images in Ionic 2

by

Mariano Alvarez

***********************************

CHANGE LOG
***********************************
I’ve updated this example to ionic 3! 😀

***********************************

Ionic is one of the most famous framework around the world to develop hybrid apps using HTML, CSS, Angular. They just released ionic 3. Guess what? We’ll use it to build our app!

One of the challenges that the hybrid apps have nowadays is deal with the offline mode. Yeah this means more work but this will give better experience to the user and the app will work faster. And come on, call an API is kind of boring and too easy 😀

LEt’s build our app

Start by installing and setting up our environment running these commands:

Install ionic cli:

npm install ionic -g

Start a new project:

ionic start OfflineApp

We are going to use ImgCache.js to cache the images. This beauty will do all the hard work. ImgCache.js is a library made for Cordova taking advantages of the HTML5 file API.

To install it in our project just run:

npm install imgcache.js --save

As you may know Ionic uses Angular 2 and typescript, so we should install a type definition for imgCache.js but unfortunately there isn’t one (someday I’ll write it or may be you will 😀 ) to avoid issues with typescript compiler add the following line in declarations.d.ts.

declare module 'imgcache.js';

Also these plugins are required:
– cordova-plugin-file
– cordova-plugin-file-transfer

and add this line to the config.xml:

import { Component, ViewChild } from '@angular/core';
import { Platform, Nav } from 'ionic-angular';
import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';
import { ImgcacheService } from '../global/services';
import { TabsPage } from '../pages/tabs/tabs';

@Component({
  templateUrl: 'app.html'
})
export class OfflineApp {

  @ViewChild('nav') nav: Nav;

  rootPage:any = TabsPage;

  constructor(platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen,
              imgcacheService: ImgcacheService) {
    platform.ready().then(() => {
      // Okay, so the platform is ready and our plugins are available.
      // Here you can do any higher level native things you might need.
      statusBar.styleDefault();
      splashScreen.hide();

      // initialize imgCache library and set root
      imgcacheService.initImgCache().then(() => {
        this.nav.setRoot(this.rootPage);
      });
    });
  }
}

We must create first a service which help us to interact with imageCache.js

import { Injectable } from '@angular/core';
import { Platform }   from 'ionic-angular';

import ImgCache       from 'imgcache.js';

/**
 * This service is charged of provide the methods to cache the images
 */
@Injectable()
export class ImgcacheService {

  public imgQueue: string[] = [];

  constructor(platform: Platform) {
    ImgCache.options.debug = true;
  }

  /**
   * Init imgCache library
   * @return {Promise}
   */
  public initImgCache(): Promise<any> {
    return new Promise((resolve, reject) => {
      if (ImgCache.ready) {
        resolve();
      } else {
        ImgCache.init(() => resolve(), () => reject());
      }
    });
  }

  /**
   * Cache images
   * @param src {string} - img source
   */
  public cacheImg(src: string): Promise<any> {
    return new Promise((resolve, reject) => {
      ImgCache.isCached(src, (path: string, success: boolean) => {
        // if not, it will be cached
        if (success) {
          ImgCache.getCachedFileURL(src,
            (originalUrl, cacheUrl) => {
              resolve(cacheUrl);
            },
            (e) => {
              reject(e)
            });
        } else {
          // cache img
          ImgCache.cacheFile(src);
          // return original img URL
          resolve(src);
        }
      });
    });
  }
}

This service has two important functions:
– initImgCache() — will init the library a return a promise
– cacheImg() — it is in charged of checking if the image is already cache. If so, it will return the cacheUrl, if not, the image will be cached but the original url will be returned.

Now We’re ready to use ImgCache.js We are going to create a directive that will cache the images.

import { Directive,
         ElementRef,
         EventEmitter,
         Input,
         Output,
         OnInit, OnDestroy, Renderer2 } from '@angular/core';
import { ImgcacheService } from '../services/';
/**
 * This directive is charge of cache the images and emit a loaded event
 */
@Directive({
  selector: '[lazy-load]'
})
export class LazyLoadDirective implements OnInit, OnDestroy {
@Input('inputSrc') src ='';
  @Output() loaded = new EventEmitter();
public loadEvent: any;
  public errorEvent: any;
constructor(public el: ElementRef,
              public imgCacheService: ImgcacheService,
              public renderer: Renderer2) {}
ngOnInit() {
    // get img element
    const nativeElement = this.el.nativeElement;
    const render = this.renderer;
    // add load listener
    this.loadEvent = render.listen(nativeElement, 'load', () => {
      render.addClass(nativeElement, 'loaded');
      this.loaded.emit();
    });
    this.errorEvent = render.listen(nativeElement, 'error', () => {
      nativeElement.remove();
    });
    // cache img and set the src to the img
    this.imgCacheService.cacheImg(this.src).then((value) => {
      render.setAttribute(nativeElement, 'src', value);
    });
  }
ngOnDestroy() {
    // remove listeners
    this.loadEvent();
    this.errorEvent();
  }
}

Here is a little explanation:

@Input inputSrc — image url.
@Output() loaded — will trigger an event when the image has loaded.
`public loadEvent: any`and `public errorEvent: any;` — save method to remove listeners.
ngOnInit() — get image element, bind load and error events and set the src when it’s retreived
ngOnDestroy() — remove listeners when the directive is destroyed

Run ionic serve to see it working on browser (use Chrome).

Find the complete example here.

https://github.com/NinjaDevsIO/offline-ionic

BTW — One thing you have to consider is that CORS must be enabled in your server otherwise the imgCache.js can’t download the images. I uploaded the images to imgur for this example

Wow Wow, That’s it? Yes!! 🎉 🎉

Comments

comments

Powered by Facebook Comments