Skip to content

octaviogarbet/ionicv3-example

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

34 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ionicv3-example

Ionic example for dev week

Contents

Overview

ionicv3-example is an Ionic 3 app created to use as a Demo for the Dev to Mobile developer talk at DevWeek. Here we will see several steps that were done to build this app. We will develop a simple app that will have:

  • A form page to create simple items, with a title, a description and a picture (from our gallery or camera)
  • A list page which will display the created items as cards. We will add the ability to share its pictures through other apps.
  • And will store the created items in order to have them available every time we open the app.

Setup

Nodejs should be installed in your dev environment. To install Ionic and Cordova run:

$ npm install -g ionic cordova

Then we chose the side menu template, so now that we have ionic cli installed, you just have to run:

$ ionic start DevWeek sidemenu

Now, to check that it works, we run:

$ cd DevWeek
$ ionic serve

Pages

In order to develop our app, we will add a new page, and transform the pages created by the template from a simple angular component, to an Ionic page. We will do the change because ionic page uses lazy loading to laod the module which improves our app performance.

New page

Now, to create a new page we will run the cli command:

$ ionic g page CreateItem

That will create under pages folder a new folder create-item and four files in it: create-item.html, create-item.ts, create-item.scss and create-item.module.ts. And we will edit create-item.html to add the menu button in the header.

<ion-header>
  <ion-navbar>
    <button ion-button menuToggle>
      <ion-icon name="menu"></ion-icon>
    </button>
    <ion-title>Create Item</ion-title>
  </ion-navbar>
</ion-header>

Component to page

Now that we have a generated page as an example, we will:

  • Add the @IonicPage() decorator to the ones generated at the "start".
  • Create the missing *.module.ts files for our pages.
  • Remove the imports from app.module.ts
  • Change the navigation calls in order to use lazy loading, that is made by removing the import of the components and just changing the ComponentName of the navigation's params and leave it just as a string. As an example, list.ts will be:
import { Component } from '@angular/core';
import { NavController, NavParams, IonicPage } from 'ionic-angular';

@IonicPage()
@Component({
  selector: 'page-list',
  templateUrl: 'list.html'
})
export class ListPage {
    ...
}

And list.module.ts will be:

import { NgModule } from '@angular/core';
import { IonicPageModule } from 'ionic-angular';
import { ListPage } from './list';

@NgModule({
  declarations: [
    ListPage
  ],
  imports: [
    IonicPageModule.forChild(ListPage),
  ],
})
export class ListPageModule {}

Then, app.module.ts:

@NgModule({
  declarations: [
    MyApp,
  ],
  imports: [
    BrowserModule,
    IonicModule.forRoot(MyApp),
  ],
  bootstrap: [IonicApp],
  entryComponents: [
    MyApp,
  ],
  providers: [
    StatusBar,
    SplashScreen,
    {provide: ErrorHandler, useClass: IonicErrorHandler}
  ]
})
export class AppModule {}

And finally app.component.ts

...
@Component({
  templateUrl: 'app.html'
})
export class MyApp {
  @ViewChild(Nav) nav: Nav;

  rootPage: any = 'HomePage';

  pages: Array<{title: string, component: any}>;

  constructor(public platform: Platform, public statusBar: StatusBar, public splashScreen: SplashScreen) {
    this.initializeApp();
    this.pages = [
      { title: 'Home', component: 'HomePage' },
      { title: 'List', component: 'ListPage' }
    ];
  }
  ...
}

Adding new components

Now, we will add a new component in order to have the item's logic and template of the list in a reusable separated component and not in the list page.

First of all, we will generate the component with the ionic cli

$ ionic g component ListItem

The second step is to add the ionic module into the generated components module in order to be able to use ionic components in our custom components.

import { NgModule } from '@angular/core';
import { ListItemComponent } from './list-item/list-item';
import { IonicModule } from 'ionic-angular';

@NgModule({
	declarations: [ListItemComponent],
	imports: [IonicModule],
	exports: [ListItemComponent]
})
export class ComponentsModule {}

After that, we will add an input to our component, that we will use later, in order to pass the data that we want to show from the list page. Our list-item.component.ts will look like:

@Component({
  selector: 'list-item',
  templateUrl: 'list-item.html'
})
export class ListItemComponent {
  @Input() profile: Profile;

  constructor() {}

  //TODO: implement feature
  shareIt() {}

  //TODO: implement feature
  removeIt() {}
}

P.S. We will add shareIt and removeIt features later. Profile is an interface we created:

export interface Profile {
  id: string,
  title: string,
  description: string,
  image: string
}

After that, we will import the generated ComponentsModule in the ListModule, so we can use the component that we have just created in our list page.

import { NgModule } from '@angular/core';
import { IonicPageModule } from 'ionic-angular';
import { ListPage } from './list';
import { ComponentsModule } from '../../components/components.module';

@NgModule({
  declarations: [
    ListPage
  ],
  imports: [
    IonicPageModule.forChild(ListPage),
    ComponentsModule
  ],
})
export class ListPageModule {}

We also will use this component in ListPage, so we will call it in our list.html and remove the all autogenerated content code (ion-header doesn't change), so the ion-content will look like:

<ion-content>
  <list-item *ngFor="let item of items" [profile]="item"></list-item>
</ion-content>

We will also change our list.ts to use the profile interface in our item list:

export class ListPage {
  items: Profile[];
  ...
}

Adding UI Components

Here is when we start to make some magic (appart from the side menu and the pages). Ionic has many UI Components that help us build the interface for our app. For example, it has tabs, action sheets, modals, popups, toolbars and more. The documentation is really good and the components are easy to add in our app. The best part is that each component has the look and feel that it should have according to the OS of the device that the app is running, so you don't have to implement additional code to add different behaviours to the Ionic components.

For example, we will add a Fixed Action Button to our list page, that will later navigate to our creat-item page; and a card in our ListItem component to display each item in a card.

FABs

If you see at FABs Component doc, it has examples of different FABs that we can add to our view. It also uses some ionicons that are part of ionic, and have a different look in iOS and Android. We will add one at the bottom right of our list page, so the list.html ion-content will look like:

<ion-content>

  <list-item *ngFor="let item of items" [profile]="item"></list-item>

  <ion-fab right bottom>
    <button ion-fab><ion-icon name="add"></ion-icon></button>
  </ion-fab>
</ion-content>

Cards

Now we take a look at the Card Component doc, and after looking at card examples we selected the Images In Cards example to be added in our ListItem component. We will add the card code to our list-item.html using the profile variable to show the info.

<ion-card>
  <img src="{{profile.image}}" />
  <ion-card-content>
    <ion-card-title>
      {{profile.title}}
    </ion-card-title>
    <p>
      {{profile.description}}
    </p>
  </ion-card-content>
  <ion-item>
    <button ion-button clear item-start (click)="removeIt()"><ion-icon color="secondary" name="trash"></ion-icon></button>
    <button ion-button clear item-end (click)="shareIt()"><ion-icon name="share"></ion-icon></button>
  </ion-item>
</ion-card>

Navigation

Ionic Navigation is what Ionic has to perform the navigation in our app. It has a NavController that can be imported into our components (including pages), which has some functions to handle the navigation. In our sidemenu project, we have the ion-nav working in our app.html, so will only use the NavController to make use of it. Navigation in ionic works as a stack, and we have 3 main basic usages for it.

  • push('PageName') -> to navigate to PageName page, adding it to the top of the stack.
  • pop() -> to navigate to the previous page and remove the top page from the stack.
  • setRoot('PageName') -> to navigate to PageName page, removing all pages from the stack and adding it as the only one in the stack. Now we will add the navigation from the list page to the create-item. In list.ts we will add:
export class ListPage {
...
  goToAdd() {
    this.navCtrl.push('CreateItemPage');
  }
}

And in list.html we will add the goToAdd call in our FAB:

  <ion-fab right bottom>
    <button ion-fab (click)="goToAdd()"><ion-icon name="add"></ion-icon></button>
  </ion-fab>

Ionic Native Integration

Ionic Native is a wrapper for Cordova/PhoneGap plugins. It helps us to add native functionality to our app, and it is really easy. To try a couple of examples, we will add a Social Sharing in order to share our items through other apps, and the Camera to take pictures or pick images from our phone data.

Social Share

Ionic Social Sharing

Next, let's add the chance to share the content of the list item to any of the social app installed on you mobile device. You can either specify which app your app wants to send data ot just configure to open the share window so the user can select any app.

The social sharing plugin, will do teh job for us.

ionic cordova plugin add cordova-plugin-x-socialsharing

npm install --save @ionic-native/social-sharing

Add it to the app Module

@NgModule({
  declarations: [
    MyApp,
  ],
  imports: [
    ...
  ],
  bootstrap: [IonicApp],
  entryComponents: [
    MyApp,
  ],
  providers: [
    StatusBar,
    SplashScreen,
    SocialSharing
  ]
})
...

Add it to the ListItemComponent and trigger it when user hit share

...
import { SocialSharing } from '@ionic-native/social-sharing';
...

@Component({
  selector: 'list-item',
  templateUrl: 'list-item.html'
})
export class ListItemComponent {
  @Input() profile: Profile;

  constructor(
    private socialSharing: SocialSharing,
    private toastCtrl: ToastController,
    private itemsProvider: ItemsProvider) {       
  }

  shareIt() {
    if (this.profile.image) {
      const param = {
        message: this.profile.description, // not supported on some apps (Facebook, Instagram)
        subject: 'the subject', // fi. for email
        files: [this.profile.image], // an array of filenames either locally or remotely
        url: undefined,
        chooserTitle: 'Pick an app', // Android only, you can override the default share sheet title,
        // appPackageName: 'com.apple.social.facebook' // Android only, you can provide id of the App you want to share with
      };
      this.socialSharing.shareWithOptions(param).then(() => {
        console.log('Share completed');
      }).catch(() => {
        console.log('Share broken');
      });
    } else {
      this.toastCtrl.create({
        message: `This card don't have image to share`,
        duration: 3000,
        position: 'middle'
      }).present();
    }
  }
  ...

There are some limitations about the content that you can set depending on the underline system (Android support some features that cannot being apply on IOS), you can check those in the plugin's repo

Camera

Now we will add the Ionic Camera Plugin and use it in our CreateItemPage. First of all, we will install it running the commands:

$ ionic cordova plugin add cordova-plugin-camera
$ npm install --save @ionic-native/camera

The second step is to add it to our AppModule:

...
import { Camera } from '@ionic-native/camera';
@NgModule({
  declarations: [
    MyApp,
  ],
  imports: [
    BrowserModule,
    IonicModule.forRoot(MyApp),
  ],
  bootstrap: [IonicApp],
  entryComponents: [
    MyApp,
  ],
  providers: [
    StatusBar,
    SplashScreen,
    SocialSharing,
    Camera,
    {provide: ErrorHandler, useClass: IonicErrorHandler}
  ]
})
export class AppModule {}

Then we add it to the component where we want to use it, as well as the function that will call the Camera plugin. We also add the ToasterController (other Ionic component) in order to give feedback to the user if something goes wrong.

...
import { Camera } from '@ionic-native/camera';
...
export class CreateItemPage {
  constructor(
    ...
    public camera: Camera,
    private toastCtrl: ToastController,
    ...) {}

  getPicture(cameraSource: boolean) {
    if (Camera['installed']()) {
      this.camera.getPicture({
        //if cameraSource is true get the image from camera, else get it from gallery
        sourceType:cameraSource ? this.camera.PictureSourceType.CAMERA : this.camera.PictureSourceType.PHOTOLIBRARY,
        destinationType: this.camera.DestinationType.DATA_URL,
        quality: 100,
        allowEdit: true,
        targetWidth: 500,
        targetHeight: 500,
        encodingType: this.camera.EncodingType.JPEG,      
        correctOrientation: true
      }).then((data) => {
        this.item.image = 'data:image/jpg;base64,' + data;
      }, (err) => {
        this.toastCtrl.create({
          message: 'Unable to take the picture',
          duration: 3000,
          position: 'middle'
        }).present();
      })
    }
  }
}

Here, we only explained how to make the Camera work in our app, but in create-item.html, create-item.ts and create-item.scss we also added: some style improvements, an action sheet to choose between camera and gallery, a form with the title and description, and a provider which will be the responsible to add the item created to the list.

Storage

Efficient key/value pair string or JSON objects, offering fall back to several techs, prioritized order depending on the platform.

Ionic Storage

As you set up the provider, you can specify the storage engines that you prefer. Add a few just to support different platforms (PWA, mobile app, etc). It uses localStorage in browsers with no IndexedDB or WebSQL support (more info about localForage).

Ionic Storage comes as part of the Ionic stuff, but if you want to use SQLite, you need to install it.

ionic cordova plugin add cordova-sqlite-storage
...
import { IonicStorageModule } from '@ionic/storage';

@NgModule({
  declarations: [
    MyApp,
  ],
  imports: [
    BrowserModule,
    IonicModule.forRoot(MyApp),
    IonicStorageModule.forRoot({
      name: '__mydb',
         driverOrder: ['indexeddb', 'sqlite', 'websql']
    })
  ]
  ...

Getting data

import { Storage } from '@ionic/storage';

@Injectable()
export class ItemsProvider {
  ...

  constructor(private storage: Storage) {
    ...
  }

  ...

// Get all the saved keys in the storage

getKeys(): Promise<string[]> {
  return this.storage.keys();
}

// Get specific value by key

getItem(key: string): Promise<Profile> {
  return this.storage.get(key);
}

Saving

private items: Profile[];
...

this.storage.set('list', this.items);

Theming

Now it's time to theme our app. The Ionic Theming doc has all that we need to know in order to apply our custom styles to our app. In order to give an example, we will override some of the ionic variables. In variables.scss we will change the colors variable and override some of the toolbar variables too:

$colors: (
  primary:    #de411b,
  secondary:  #48535b,
  danger:     #f53d3d,
  light:      #f4f4f4,
  dark:       #222
);

$toolbar-background: map-get($colors, primary);
$toolbar-border-color: map-get($colors, secondary);

Authors