Ionic2 multi columns Image Grid


I had a requirement to have an image grid-like image gallery.



I gone through the many tutorials and blogs. All were not suitable for my purpose.

  1. Some works with square sized images.
  2. Some works with hard-coded grid tile width.
  3. Some works on responsive design using fixed with image tile. I could not use this one because I need to use virtual-scroll.
  4. All work with the known number of items. All number of rows and columns are decided in the beginning.
I wanted to add image delete image and update image at the run time via some action without compromising the performance (with 10,000 image items). After searching here and there on Internet finally, I decided to sit calm and find the solution myself. After few hours a fairly simple solution came on the table.


Let's try with a sample application.


Create a blank App with the name 'ImageGrid'

ionic start ImageGrid

this will create the starting project. We will focus on the home.html, home.ts and home.scss only.

Image grid can be visualized as a 2D matrix with a fixed number of columns and variable numbers of row.
As I came from C/C++ background and know that internally 2D array is a contiguous memory allocation. So I decided to handle the 2D image matrix like a simple array.

Check the basic concept

// Create a type, you can add more fields to this type as per your requirement.
interface FileObject {
  public thumbnail: string;
}

const _grid_columns_count: number = 3; // Number of columns in image grid

//Get a specific item by row and column
yourFunction(row: number, col: number) {
  let i = row * _grid_columns_count + col;
}
// Here i is index by which you can query an item by row and column.
Full Source code
Home.ts
import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';

interface FileObject {
  thumbnail: string;
}

const _grid_columns_count: number = 3; // Number of columns in image grid

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {

  private columns: Array<number>;
  private rows: Array<number>;
  private fileObjects: FileObject[];


  constructor(public navCtrl: NavController) {
    // Create column array for number of columns you want
    this.columns = new Array(_grid_columns_count);

    // This array will hold the number of rows
    this.rows = new Array<number>();

    // This array will hold all the items in the grid.
    // Add a item by appending or inserting
    // Remove a item by delete your item from the array
    this.fileObjects = new Array<FileObject>();
  }

  // Add item
  addItem(obj: FileObject) {
    this.fileObjects.push(obj);

    // true of if condition indicates that we need a new row.
    if ((this.rows.length * _grid_columns_count) < this.fileObjects.length) {
      this.rows.push(this.rows.length);
    }
  }

  // Get an item by row and column
  getItemThumbnail(row: number, col: number) {
    let i = row * _grid_columns_count + col;

    if (i < this.fileObjects.length) {
      return this.fileObjects[i].thumbnail;
    }
    else {
      return "no found";
    }
  }

  isValid(row: number, col: number): boolean {
    let i = row * _grid_columns_count + col;
    return (i < this.fileObjects.length);
  }

  onTapImage(row, col) {
    let i = row * _grid_columns_count + col;

    if (i < this.fileObjects.length) {
      alert(this.fileObjects[i].thumbnail);
    }
  }

  getTileHeight(): string {
    return "100px;"
  }

  /*
  This is a method just to simulate how we can items
  change the image path as per your wish
*/
  addImages() {
    for (let i = 0; i < 8; ++i) {
      let obj: FileObject = {
        thumbnail: 'url("../assets/icon/image.jpg")'
      }

      this.addItem(obj);
    }
  }
}
Home.html
<ion-header>
  <ion-navbar color="dark">
    <ion-title>
      {{fileObjects.length}} files
    </ion-title>
  </ion-navbar>
</ion-header>

<ion-content padding style="background-color:#222">

  <ion-list [virtualScroll]="rows" approxItemHeight="{{getTileHeight()}}" no-margin>
    <ion-row *virtualItem="let row" class="item-row">
      <ion-col *ngFor="let column of columns; let col = index">
        <div *ngIf="isValid(row, col)" [style.background-image]="getItemThumbnail(row, col)" class="image-background" (tap)="onTapImage(row, col)">
        </div>
      </ion-col>
    </ion-row>
  </ion-list>

</ion-content>

<ion-footer>
  <ion-toolbar color="dark" style="text-align:center;">
    <!-- Button just to trigger the adding items -->
    <button ion-button round (tap)="addImages()">Add Images</button>
  </ion-toolbar>
</ion-footer>
Home.scss
page-home {
    .item-row {
        width: 100%;
        height: 120px;
    }
    .image-background {
        border: 2px solid #222;
        background-color: rgba(92, 90, 87, 0.644);
        background-size: cover;
        background-position: center;
        background-repeat: no-repeat;
        height: 100%;
    }
}


You can find the working project at Github
https://github.com/Anil8753/ImageGrid


Emoticon Emoticon