Build A Photo Gallery app with Ionic2, Cloudinary, NodeJs And MongoDB part 1

ionic 2 photo upload with nodejs

Overview

Uploading files and images in ionic app to some remote server is without doubt one of the most required functions.

This two parts article will show you how images can be upload to Cloudinary server via NodeJS API from ionic2 app.

Cloudinary is a cloud-based service that provides an end-to-end image management solution including uploads, storage, administration, image manipulation, and delivery.

You can easily upload images to the cloud, automatically perform smart image manipulations without installing any complex software. All your images are then seamlessly delivered through a fast CDN, optimized and using industry best practices.

Cloudinary offers comprehensive APIs and administration capabilities and is easy to integrate with new and existing web and mobile applications.

In this first part, am going to create the client part of our application which the Ionic2 app, take photo with camera, or choose existing photo from gallery and send it to a remote server.

In the second part I will create the NodeJS API will be done.

Setting up the Ionic Project

Lets start by creating a blank Ionic2 project.
ionic start ionic-cloudinary blank --v2
ionic platform add android
Now that the project has been created and android platform has been added, lets install all the native plugins the app required:
  1. cordova-plugin-file
  2. cordova-plugin-file-transfer
  3. cordova-plugin-camera
ionic plugin add cordova-plugin-file
ionic plugin add cordova-plugin-file-transfer
ionic plugin add cordova-plugin-camera

Now that we have installed all the plugins needed for this project, lets create the upload picture page by running the following command in our command prompt:
ionic generate page newpost

Project Structure 

The application structure should look like the one bellow:



We will start modifying the home page to display the list of images fetched from the server using angularjs http service.
The final home page markup should look like so:

  
    
      Gallery
    
    
      
    
  


  
    
    
      
        {{item.title}}
      
      {{item.description}}
    
  
  



As you can see, there nothing complex here, we just used ngRepeat to loop through the array of photos and render them withing ion-card component.

The next step is to wire up the code that will be responsible for fetching of the images in home.ts file like so:


import { Component } from '@angular/core';
import { NavController, LoadingController } from 'ionic-angular';
import { Newpost } from '../../pages/newpost/newpost';
import { PostService } from '../../providers/post-service';

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {
  posts: any;
  constructor(public navCtrl: NavController,
    private postService: PostService, private loadingCtrl: LoadingController
  ) {


  }

  ionViewDidLoad() {
    this.getPosts();
  }

  getPosts() {
    let loader = this.loadingCtrl.create({
      content: "Loading Photos..."
    });
    loader.present();
    this.postService.getPosts().subscribe((val) => {
      this.posts = val.posts;
      loader.dismiss();
    });
  }




  addNewPhoto() {
    this.navCtrl.push(Newpost);
  }

}



In the home.ts file has two imports statement to NewPost page and PostService which have not created. Now let add the PostService by typing this ionic cli command:

ionic generate provider PostService

Modify the post-service.ts file like so:

import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import 'rxjs/add/operator/map';

@Injectable()
export class PostService {
  posts: any;
  
  constructor(public http: Http) {

  }

  getPosts() {
    return this.http.get('https://photocloudapp.herokuapp.com/api/v1/posts')
      .map(res => res.json());
  }

 
}

As you can see the only method we have here is getPosts. This method just return angular's http get response from our API endpoint (We are going to create the API in the second part of this article)

Let's modify the newpost html page to look like so:



  
    
Post Title Description
There is nothing complex about this view. As you can see we are also going to save the picture title and description.

We are going to do a bit of work in the newpost.ts file.

The first task is to import the required modules on this file. As you know we are going to be using some native plugins, so let's import those plugins:

import { File, Camera, Transfer } from 'ionic-native';


We also need to import LoadingController and ActionSheetController like so:

import { NavController, LoadingController, ActionSheetController } from 'ionic-angular';

The final code in the newpost.ts look like this:


 
import { Component } from '@angular/core';
import { NavController, LoadingController, ActionSheetController } from 'ionic-angular';
import { File, Camera, Transfer } from 'ionic-native';
import { HomePage } from '../home/home';

declare var cordova: any;

@Component({
  selector: 'page-newpost',
  templateUrl: 'newpost.html'
})
export class Newpost {
  postTitle: any;
  desc: any;
  imageChosen: any = 0;
  imagePath: any;
  imageNewPath: any;

  constructor(public navCtrl: NavController,
    public actionSheet: ActionSheetController,
    private loadingCtrl: LoadingController) {

  }

  ionViewDidLoad() {

  }


  uploadPhoto() {
    let loader = this.loadingCtrl.create({
      content: "Please wait..."
    });
    loader.present();

    let filename = this.imagePath.split('/').pop();
    let options = {
      fileKey: "file",
      fileName: filename,
      chunkedMode: false,
      mimeType: "image/jpg",
      params: { 'title': this.postTitle, 'description': this.desc }
    };


    const fileTransfer = new Transfer();

    fileTransfer.upload(this.imageNewPath, 'https://photocloudapp.herokuapp.com/api/v1/post/upload',
      options).then((entry) => {
        this.imagePath = '';
        this.imageChosen = 0;
        loader.dismiss();
        this.navCtrl.setRoot(HomePage);
      }, (err) => {
        alert(JSON.stringify(err));
      });
  }

  chooseImage() {

    let actionSheet = this.actionSheet.create({
      title: 'Choose Picture Source',
      buttons: [
        {
          text: 'Gallery',
          icon: 'albums',
          handler: () => {
            this.actionHandler(1);
          }
        },
        {
          text: 'Camera',
          icon: 'camera',
          handler: () => {
            this.actionHandler(2);
          }
        },
        {
          text: 'Cancel',
          role: 'cancel',
          handler: () => {
            console.log('Cancel clicked');
          }
        }
      ]
    });

    actionSheet.present();
  }


  //}

  actionHandler(selection: any) {
    var options: any;

    if (selection == 1) {
      options = {
        quality: 75,
        destinationType: Camera.DestinationType.FILE_URI,
        sourceType: Camera.PictureSourceType.PHOTOLIBRARY,
        allowEdit: true,
        encodingType: Camera.EncodingType.JPEG,
        targetWidth: 500,
        targetHeight: 500,
        saveToPhotoAlbum: false
      };
    } else {
      options = {
        quality: 75,
        destinationType: Camera.DestinationType.FILE_URI,
        sourceType: Camera.PictureSourceType.CAMERA,
        allowEdit: true,
        encodingType: Camera.EncodingType.JPEG,
        targetWidth: 500,
        targetHeight: 500,
        saveToPhotoAlbum: false
      };
    }

    Camera.getPicture(options).then((imgUrl) => {

      var sourceDirectory = imgUrl.substring(0, imgUrl.lastIndexOf('/') + 1);
      var sourceFileName = imgUrl.substring(imgUrl.lastIndexOf('/') + 1, imgUrl.length);
      sourceFileName = sourceFileName.split('?').shift();
      File.copyFile(sourceDirectory, sourceFileName, cordova.file.externalApplicationStorageDirectory, sourceFileName).then((result: any) => {
        this.imagePath = imgUrl;
        this.imageChosen = 1;
        this.imageNewPath = result.nativeURL;

      }, (err) => {
        alert(JSON.stringify(err));
      })

    }, (err) => {
      alert(JSON.stringify(err))
    });

  }
  
}


And the last thing here is the sass file for the two pages: Home.scss file
page-home {
  ion-card {
    margin: 0;
    width: 100%;
    border-radius: 0;
    ion-card-content {
      padding: 3px 5px 10px 3px !important;
    }
  }
}

NewPost.scss file
page-newpost {
ion-card{
    margin: 3px !important;
    width: calc(100% - 6px) !important;
    .uploadWrap{
        ion-icon{
            color:#777;
            font-size: 150px;
            text-align: center !important;
            display: block;
        }
    }
    button{
        height: 40px !important;
    }
}
}


You can find the entire source code of this article here.

In the next post we are going to sign up for free cloudinary account, create our Nodejs API and deploy to Heroku.

I hope this helps. Happy coding.

No comments

Powered by Blogger.