Ionic 3 – Prikaz Google mape

U ovom ću blog postu pokazati kako integrirati Google mape unutar Ionic aplikacije koristeći JavaScript SDK.

Zašto Google Maps?

Zato što je to najjednostavniji i najbolji način za prikaz mapa sa dosta mogućnosti i odličnom dokumentacijom + što je korištenje Google mapa besplatno do određene razine.

Native ili JavaScript SDK?

U ovom slučaju koristit ćemo JavaScript SDK jer nam je to brži i jednostavniji način za razvoj i testiranje. Znači, kako razvijamo funkcionalnosti Google mape unutar Ionic aplikacije istovremeno ih možemo testirati unutar web preglednika dok bi u slučaju korištenja Native SDK morali sve testirati na uređaju.

Pod pretpostavkom da već imate pokrenut Ionic projekt možemo krenuti dalje.

Prikaz Google mape

Za početak ćemo uvesti JavaScript SDK u index.html datoteku na način da dodamo <script> tag na sljedeći način

<script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyBnJkTG_-ZSxxXwseJTZcvgFscpcFM1WFw"></script>
<!-- cordova.js required for cordova apps -->
<script src="cordova.js"></script>

U slučaju da ne unesete Api Key dobit ćete poruku o grešci “Google Maps API warning: NoApiKeys“, ali će mape i dalje raditi.

Google Maps API warning: NoApiKeys

Sada se možemo prebaciti na stranicu u našoj Ionic aplikaciji koja će služiti za prikaz Google mape. U ovom slučaju to će biti home.html

<ion-header>
  <ion-navbar>
    <ion-title>
      Ionic3GoogleMaps
    </ion-title>
  </ion-navbar>
</ion-header>

<ion-content>

<div #map id="map"></div>

</ion-content>

ID “map” služi nam samo kako bi mogli napraviti sljedeće

page-home {
  #map {
      width: 100%;
      height: 100%;
  }
}

Za sada još uvijek ne vidimo mapu jer nismo kontoleru rekli što da napravi, gdje i pomoću čega tj. naša datoteka home.ts trenutno izgleda ovako

import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';

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

  constructor(public navCtrl: NavController) {

  }

}

Međutim, nakon što uvezemo dodatne elemente i kreiramo funkciju koja će prikazati zadanu mapu stanje će se promijeniti

import { Component, ViewChild, ElementRef } from '@angular/core';
import { NavController } from 'ionic-angular';

declare var google;

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {
  @ViewChild('map') mapElmt: ElementRef;
  map: any;
  constructor(public navCtrl: NavController) {

  }

  ionViewDidLoad(){
    this.loadMap();
  }

  loadMap(){
    let latLng = new google.maps.LatLng('45.287906','18.805678');
    let mapOptions = {
      center:latLng,
      zoom:11,
      mapTypeId: google.maps.MapTypeId.ROADMAP
    }

    this.map = new google.maps.Map(this.mapElmt.nativeElement, mapOptions);

  }

}

Sada već možemo nešto konkretno vidjeti na ekranu. Imamo mapu koja prikazuje Vinkovce prema long i lat parametrima.

Ionic3 Google Maps

Dodavanje markera

Mapa prikazana na ranije definiran način nije pretjerano korisna i zato ćemo dodati markere kako bi se točnije znalo što želimo naglasiti i što zapravo prikazujemo. Za ovaj ću primjer odabrati četiri lokacije vezane uz grad Vinkovce i njih ću označiti na Google mapi.

import { Component, ViewChild, ElementRef } from '@angular/core';
import { NavController } from 'ionic-angular';

declare var google;

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {
  @ViewChild('map') mapElmt: ElementRef;
  map: any;

   mojeLokacije = [
     {
      'name':'Stadion HNK Cibalia',
      'lat':'45.283666',
      'lng':'18.797286'
     },
     {
      'name':'Opća županijska bolnica Vinkovci',
      'lat':'45.292581',
      'lng':'18.818271'
     },
     {
      'name':'Jezero Banja, Vinkovci',
      'lat':'45.286323',
      'lng':'18.784025'
     },
     {
      'name':'Željeznički kolodvor (postaja) Vinkovci',
      'lat':'45.300063',
      'lng':'18.802725'
     }
   ]

  constructor(public navCtrl: NavController) {

  }

  ionViewDidLoad(){
    this.loadMap();
  }

  loadMap(){
    let latLng = new google.maps.LatLng('45.286674','18.802124');
    let mapOptions = {
      center:latLng,
      zoom:11,
      mapTypeId: google.maps.MapTypeId.ROADMAP
    }

    this.map = new google.maps.Map(this.mapElmt.nativeElement, mapOptions);

    for (let lokacija of this.mojeLokacije){
        this.addMarker(lokacija, lokacija['name']);
    }

  }

  addMarker(posInfo,info){
    let position = new google.maps.LatLng(posInfo['lat'],posInfo['lng']);

    let marker = new google.maps.Marker({
      map: this.map,
      animation: google.maps.Animation.DROP,
      position: position
    });
  }

}

Kada osvježimo ekran možemo vidjeti animirane markere za sve četiri lokacije.

Ionic3 Google Maps Markers

Dodatne informacije na markerima

Iako se ranije prikazana mapa već sada može smatrati donekle funkcionalnom ipak joj nedostaje jedan važan detalj, a to su informacije o tome što svaki pojedini marker točno prikazuje.

Zato ćemo sada za svaki marker napraviti skočni prozorčić koji će se prikazati kada se klikne na pojedini marker i na taj ćemo način prikazati naziv lokacije.

import { Component, ViewChild, ElementRef } from '@angular/core';
import { NavController } from 'ionic-angular';

declare var google;

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {
  @ViewChild('map') mapElmt: ElementRef;
  map: any;

   mojeLokacije = [
     {
      'name':'Stadion HNK Cibalia',
      'lat':'45.283666',
      'lng':'18.797286'
     },
     {
      'name':'Opća županijska bolnica Vinkovci',
      'lat':'45.292581',
      'lng':'18.818271'
     },
     {
      'name':'Jezero Banja, Vinkovci',
      'lat':'45.286323',
      'lng':'18.784025'
     },
     {
      'name':'Željeznički kolodvor (postaja) Vinkovci',
      'lat':'45.300063',
      'lng':'18.802725'
     }
   ]

  constructor(public navCtrl: NavController) {

  }

  ionViewDidLoad(){
    this.loadMap();
  }

  loadMap(){
    let latLng = new google.maps.LatLng('45.286674','18.802124');
    let mapOptions = {
      center:latLng,
      zoom:11,
      mapTypeId: google.maps.MapTypeId.ROADMAP
    }

    this.map = new google.maps.Map(this.mapElmt.nativeElement, mapOptions);

    for (let lokacija of this.mojeLokacije){
        this.addMarker(lokacija, lokacija['name']);
    }

  }

  addMarker(posInfo,info){
    let position = new google.maps.LatLng(posInfo['lat'],posInfo['lng']);

    let marker = new google.maps.Marker({
      map: this.map,
      animation: google.maps.Animation.DROP,
      position: position
    });
    let markerInfo = info;
    this.addInfo(marker, markerInfo);
  }

  addInfo(marker, content){
    let infoWindow = new google.maps.InfoWindow({
      content: content
    });

    google.maps.event.addListener(marker, 'click', () => {
     infoWindow.open(this.map, marker);
    })
  }

} 

U nastavku možemo vidjeti markere s dodatnim informacijama.
Ionic3 Google Maps Markers Info

Popis lokacija s fokusom

Sada ćemo napraviti popis lokacija koji će se nalaziti ispod same mape. Klikom na neku od lokacija mapa će se automatski pozicionirati na odabranu lokaciju.

Za početak ćemo napraviti malo mjesta ispod mape

page-home {
  #map {
      width: 100%;
      height: 70%;
  }
}

Nakon toga napravit ćemo pripremu za ispis lokacija

<ion-header>
  <ion-navbar>
    <ion-title>
      Ionic3GoogleMaps
    </ion-title>
  </ion-navbar>
</ion-header>

<ion-content>

<div #map id="map"></div>
<ion-list>
  <button ion-item (click)="focusMap(lokacija)" *ngFor="let lokacija of mojeLokacije">
  <ion-icon name="locate" item-left small></ion-icon>
  {{lokacija.name}}
  </button>
</ion-list>

</ion-content>

I sada smo spremni za kreiranje funkcije koja će nam omogućiti fokus na odabranu lokaciju

import { Component, ViewChild, ElementRef } from '@angular/core';
import { NavController } from 'ionic-angular';

declare var google;

@Component({
  selector: 'page-home',
  templateUrl: 'home.html'
})
export class HomePage {
  @ViewChild('map') mapElmt: ElementRef;
  map: any;

   mojeLokacije = [
     {
      'name':'Stadion HNK Cibalia',
      'lat':'45.283666',
      'lng':'18.797286'
     },
     {
      'name':'Opća županijska bolnica Vinkovci',
      'lat':'45.292581',
      'lng':'18.818271'
     },
     {
      'name':'Jezero Banja, Vinkovci',
      'lat':'45.286323',
      'lng':'18.784025'
     },
     {
      'name':'Željeznički kolodvor (postaja) Vinkovci',
      'lat':'45.300063',
      'lng':'18.802725'
     }
   ]

  constructor(public navCtrl: NavController) {

  }

  ionViewDidLoad(){
    this.loadMap();
  }

  loadMap(){
    let latLng = new google.maps.LatLng('45.286674','18.802124');
    let mapOptions = {
      center:latLng,
      zoom:11,
      mapTypeId: google.maps.MapTypeId.ROADMAP
    }

    this.map = new google.maps.Map(this.mapElmt.nativeElement, mapOptions);

    for (let lokacija of this.mojeLokacije){
        this.addMarker(lokacija, lokacija['name']);
    }

  }

  addMarker(posInfo,info){
    let position = new google.maps.LatLng(posInfo['lat'],posInfo['lng']);

    let marker = new google.maps.Marker({
      map: this.map,
      animation: google.maps.Animation.DROP,
      position: position
    });
    let markerInfo = info;
    this.addInfo(marker, markerInfo);
  }

  addInfo(marker, content){
    let infoWindow = new google.maps.InfoWindow({
      content: content
    });

    google.maps.event.addListener(marker, 'click', () => {
     infoWindow.open(this.map, marker);
    })
  }

  focusMap(lokacija){
   let position = new google.maps.LatLng(lokacija['lat'],lokacija['lng']);
   this.map.setCenter(position);
   this.map.setZoom(15);
  }

}

Konačni rezultat je vidljiv u nastavku
Ionic3 Google Maps Markers Zoom

Zaključak

Ovo je samo dio onoga što se može postići koristeći Google Maps API. Dalje sve ovisi o specifičnim potrebama vaše Ionic aplikacije, ali vjerujem da možete postići sve što želite jer dokumentacija je poprilično detaljna i za svakoga se može pronaći nešto korisno.

{
  "name": "Ionic3-GoogleMaps",
  "version": "0.0.0",
  "author": "Tomislav Stanković",
  "homepage": "https://www.tomislavstankovic.com/",
  "private": true,
  "scripts": {
    "clean": "ionic-app-scripts clean",
    "build": "ionic-app-scripts build",
    "lint": "ionic-app-scripts lint",
    "ionic:build": "ionic-app-scripts build",
    "ionic:serve": "ionic-app-scripts serve"
  },
  "dependencies": {
    "@angular/common": "4.0.2",
    "@angular/compiler": "4.0.2",
    "@angular/compiler-cli": "4.0.2",
    "@angular/core": "4.0.2",
    "@angular/forms": "4.0.2",
    "@angular/http": "4.0.2",
    "@angular/platform-browser": "4.0.2",
    "@angular/platform-browser-dynamic": "4.0.2",
    "@ionic-native/core": "3.6.1",
    "@ionic-native/splash-screen": "3.6.1",
    "@ionic-native/status-bar": "3.6.1",
    "@ionic/storage": "2.0.1",
    "ionic-angular": "3.1.1",
    "ionicons": "3.0.0",
    "rxjs": "5.1.1",
    "sw-toolbox": "3.4.0",
    "zone.js": "^0.8.10"
  },
  "devDependencies": {
    "@ionic/app-scripts": "1.3.7",
    "typescript": "~2.2.1"
  },
  "cordovaPlugins": [
    "cordova-plugin-whitelist",
    "cordova-plugin-console",
    "cordova-plugin-statusbar",
    "cordova-plugin-device",
    "ionic-plugin-keyboard",
    "cordova-plugin-splashscreen"
  ],
  "cordovaPlatforms": [],
  "description": "Ionic3GoogleMaps: An Ionic project"
}

Kako ubrzati pokretanje Ionic 3 aplikacije?

Ako koristite Ionic 2 ili Ionic 3 mogli ste primijetiti dugo vrijeme pokretanja aplikacije, kako na iOS tako i na Android platformi. Ja sam ga definitivno primijetio, a vidim da su i mnogi drugi.

Vrijeme potrebno da se aplikacija pokrene nakon Splash Screena kretalo se između 11 i 15 sekundi što nikako nije dobro za korisničko iskustvo.

Uzrok

Kao zadano, Ionic 2 & 3 projekti su u non-AoT stanju što znači da su uvijek u stanju za razvoj (state of development) i to je dobro i korisno za testiranje i rješavanje problema (debugging), ali nije dobro za kreiranje verzija aplikacija koje će koristiti krajnji korisnik.

Builds are now always development (non-AoT) by default. To enable prod builds, use the –prod option. – izvor

AoT (Ahead-of-Time) je termin za build process koji kompajlira predloške i minimizira JS datoteke kako bi se poboljšale performanse aplikacije prilikom pokretanja. Kompajler se pokreće samo prvi put prilikom kreiranja .APK-a tj. .IPA-a tj. ne pokreće se svaki prilikom pokretanja aplikacije i time se postiže veća brzina pokretanja aplikacije i prikaza predložaka. I to je upravo ono što kao developeri želimo.

Problem se javlja zbog toga što je Ionic tim odabrao da zadano stanje bude suprotno od ranije opisanog procesa. I to je razlog sporog pokretanja aplikacije. Može se činiti kao sitnica, ali i kao sitnica koja može izluditi developera koji može krenuti u proces preoptimizacije svoje aplikacije bez uspjeha.

Rješenje –prod

Rješenje svih problema sadržano je u nastavku –prod

//iOS
ionic emulate ios --prod
ionic build ios --prod
ionic run ios --prod

//Android
ionic emulate android --prod
ionic build android --prod
ionic run android --prod

Nakon dodavanja –prod nastavka možete primijetiti drastično ubrzanje prilikom pokretanja aplikacije s 11 do 15 sekundi na 2-3 sekunde.

Pretpostavimo da želite ubrzati proces pokretanja, ali ne želite minimizirati JS datoteke kako bi mogli jednostavnije testirati i rješavati probleme tj. raditi debugging. U tom slučaju možete koristiti – -aot nastavak.

//iOS
ionic emulate ios --aot
ionic run ios --aot

//Android
ionic emulate android --aot
ionic run android --aot

Na taj način koristite Ahead-of-Time kompajliranje, ali ne minimizirate JS datoteke.

Što ćete od ranije navedenog odabrati ovisi o vašim potrebama u određenom trenutku.

Zaključak

AoT čini drastičnu razliku u vremenu potrebnom za pokretanje Ionic aplikacije. Ionic tim je odabrao non-AoT kao zadano kako bi developerima olakšao proces razvoja što nove developere može odbiti od korištenja Ionic Frameworka jer im neće biti jasno zašto im je aplikacija koju su tek kreirali tako spora.

Ionic 3 – AdMob monetizacija

Prije nego krenete s izradom mobilne aplikacije za pretpostaviti je da imate nekakav cilj koji želite tom aplikacijom postići. Jedan od tih ciljeva može biti zarada.

U ovom ću blog postu prikazati kako u Ionic aplikaciju dodati AdMob oglase pomoću kojih možete zaraditi na svojoj aplikaciji.

Što je AdMob?

AdMob je Googleova platforma za monetizaciju mobilnih aplikacija. Ono što je Google AdSense/AdWords za web stranice to je AdMob za mobilne aplikacije. Dostupan je za iOS i Android aplikacije.

Kreiranje oglasa

AdMob kreiranje oglasa Na adresi https://apps.admob.com/ trebate kreirati oglasnu jedinicu koja će se prikazivati u vašoj Ionic aplikaciji.

Na slici sam zaokružio što točno trebate ubaciti u aplikaciju kako bi se oglas prikazao.

Dodavanje AdMob plugina

Plugin ćete dodati naredbama:

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

Sada referencu na plugin trebate dodati u app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { ErrorHandler, NgModule } from '@angular/core';
import { IonicApp, IonicErrorHandler, IonicModule } from 'ionic-angular';
import { SplashScreen } from '@ionic-native/splash-screen';
import { StatusBar } from '@ionic-native/status-bar';

import { MyApp } from './app.component';
import { HomePage } from '../pages/home/home';

import { AdMob } from '@ionic-native/admob';

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

Sada ste spremni za implementaciju oglasa.

Implementacija

U ovom primjeru oglas ćemo implementirati u app.component.ts što znači da će nam se oglas prikazivati na svim stranicama aplikacije.

import { Component } from '@angular/core';
import { Platform } from 'ionic-angular';
import { StatusBar } from '@ionic-native/status-bar';
import { SplashScreen } from '@ionic-native/splash-screen';
import { AdMob } from '@ionic-native/admob';

import { HomePage } from '../pages/home/home';

interface AdMobType {
  banner: string,
  interstitial: string
};

@Component({
  templateUrl: 'app.html'
})
export class MyApp {
  rootPage:any = HomePage;

  constructor(platform: Platform, _statusBar: StatusBar, _splashScreen: SplashScreen, private _admob: AdMob) {
    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();
  
     var admobid: AdMobType;
  if( /(android)/i.test(navigator.userAgent) ) { // for android & amazon-fireos
    admobid = {
      banner: 'ca-app-pub-xxxxxxxxxxxxxxxx/xxxxxxxxxx', 
      interstitial: 'ca-app-pub-xxxxxxxxxxxxxxxx/xxxxxxxxxx'
    };
  } else if(/(ipod|iphone|ipad)/i.test(navigator.userAgent)) { // for ios
    admobid = {
      banner: 'ca-app-pub-xxxxxxxxxxxxxxxx/xxxxxxxxxx', 
      interstitial: 'ca-app-pub-xxxxxxxxxxxxxxxx/xxxxxxxxxx'
    };
  } else { // for windows phone
    admobid = {
      banner: 'ca-app-pub-xxxxxxxxxxxxxxxx/xxxxxxxxxx', 
      interstitial: 'ca-app-pub-xxxxxxxxxxxxxxxx/xxxxxxxxxx'
    };
  }

  this._admob.createBanner({
    adId: admobid.banner,
    isTesting: true,
    autoShow: true
  })
  
  this._admob.prepareInterstitial({
    adId: admobid.interstitial,
    isTesting: true,
    autoShow: false
  })

    });
  }
}

isTesting: true neka stoji sve dok radite na aplikaciji, a false možete staviti kada aplikacije ide u produkciju.

Banner na dnu stranice izgleda ovako

AdMob - Banner na dnu stranice

Sljedeći primjer tiče se oglasa koji se prikazuje preko cijele stranice aplikacije. To možete implementirati nakon neke korisnikove akcije. Za sada ćemo taj oglas prikazati klikom na gumb.

import { Component } from '@angular/core';
import { NavController } from 'ionic-angular';
import { AdMob } from '@ionic-native/admob';

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

  constructor(private _admob: AdMob) {

  }

  showInterstitials(){
    if (AdMob) this._admob.showInterstitial();
  }

}

Prikaz oglasa

<ion-header>
  <ion-navbar>
    <ion-title>
      Ionic 3 Aplikacija - AdMob
    </ion-title>
  </ion-navbar>
</ion-header>

<ion-content padding>
  Ovo je primjer Ionic 3 aplikacije koja sadržava AdMob oglase.
  <ul>
    <li>Više o AdMob oglasima na adresi https://www.google.com/admob/index.html</li>
  </ul>

  <button ion-button (click)="showInterstitials()">Prikaži oglas preko cijele stranice</button>
</ion-content>

Oglas preko cijele stranice u konačnici izgleda ovako

AdMob - oglas preko cijele stranice

Zaključak

I to je sve! Možete krenuti zarađivati. 🙂

Ionic 2 & 3 – SQLite servis

Ovaj blog post je nastavak prethodnog pod naslovom Ionic 2SQLite za trajno spremanje podataka. Iako sam ondje već obradio temu spremanja podataka u lokalnu bazu podataka i prikaz tih podataka kada aplikacija nije povezana s mrežom važno je znati da postoji i bolji način kako to napraviti.

Kreiranjem SQLite servisa svu logiku vezanu uz kreiranje i korištenje lokalne baze podataka imamo na jednom mjestu.

Instalacija SQLite plugina

Plugin instalirate naredbama

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

Više o SQLite pluginu na adresi https://github.com/litehelpers/Cordova-sqlite-storage

Kreiranje SQLite servisa

Servis kreirate naredbom

$ ionic g provider MyData

Cilj ovog DatabaseService servisa je da na jednom mjestu kreiramo sve potrebne tablice te da ih uz što manje ponavljanja koda koristimo u ostatku aplikacije.

Unutar konstuktora kreiramo tablice mojaTablica i mojaDrugaTablica.

Funkcionalnosti poput spremanja u lokalnu bazu, čitanja iz nje i brisanja spremamo u posebne funkcije, kao što su npr saveTablicaToSqlite(), getTablicaMyOfflineData() i deleteTablicaMyOfflineData(), koje onda možemo pozivati u ostatku aplikacije.

import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import 'rxjs/add/operator/map';
import { Platform } from 'ionic-angular';
import { SQLite, SQLiteObject } from '@ionic-native/sqlite';

@Injectable()
export class DatabaseService {

  db:any;
  private isOpen: boolean;

  //Konstruktor početak
  constructor(public _http: Http, public _platform: Platform, private sqlite: SQLite) {
    console.log('Hello Database Provider');

    this._platform.ready().then(() => {

     if(!this.isOpen) {
            this.sqlite = new SQLite();
            this.sqlite.create({name: "baza.db", location: "default"}).then((db: SQLiteObject) => {
                 //Prva tablica
                 db.executeSql('CREATE TABLE IF NOT EXISTS mojaTablica (mojaTablicaId INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, user_name TEXT, location TEXT)', {})
                 .then(() => console.log('Executed SQL - PrvaTablica'))
                 .catch(e => console.log(e));
                 //Druga tablica
                 db.executeSql('CREATE TABLE IF NOT EXISTS mojaDrugaTablica (mojaDrugaTablicaId INTEGER PRIMARY KEY AUTOINCREMENT, firstname TEXT, lastname TEXT)', {})
                 .then(() => console.log('Executed SQL - DrugaTablica'))
                 .catch(e => console.log(e));
             })
             .catch(e => console.log(e));
     } 
     

     });
  }
  //Konstruktor kraj
   //////////Tablica\\\\\\\\\\
   //SPREMI
    public saveTablicaToSqlite(DataArray){
	  this.sqlite.create({
      name: 'baza.db',
      location: 'default'
      })
      .then((db: SQLiteObject) => {
           db.executeSql("INSERT INTO mojaTablica (name, user_name, location) VALUES (?,?,?)", [DataArray.name, DataArray.user_name, DataArray.location]).then((data) => {
           console.log("INSERTED: " + JSON.stringify(data));
           }, (error) => {
           console.log("ERROR kod inserta: " + JSON.stringify(error.err));
        });
	  })
     .catch(e => console.log(e));
    }
    //DOHVATI
    public getTablicaMyOfflineData() {
        return new Promise((resolve, reject) => {
          this.sqlite.create({
          name: 'baza.db',
          location: 'default'
          })
          .then((db: SQLiteObject) => {
            db.executeSql("SELECT * FROM mojaTablica", []).then((data) => {
                let DataArray = [];
                if(data.rows.length > 0) {
                    for(let i = 0; i < data.rows.length; i++) {
                        DataArray.push({
                            name: data.rows.item(i).name,
                            user_name: data.rows.item(i).user_name,
                            location: data.rows.item(i).location
                        });
                    }
                }
                resolve(DataArray);
            }, (error) => {
                reject(error);
            });
          })
         .catch(e => console.log(e));
        });
    }
    //OBRIŠI
    public deleteTablicaMyOfflineData(){
	  this.sqlite.create({
      name: 'baza.db',
      location: 'default'
      })
      .then((db: SQLiteObject) => {
           db.executeSql("DELETE FROM mojaTablica",[]).then((data) => {
           console.log("DELETED: " + JSON.stringify(data));
           }, (error) => {
           console.log("ERROR kod brisanja: " + JSON.stringify(error.err));
        });
	 })
     .catch(e => console.log(e));
    }
   //////////DrugaTablica\\\\\\\\\\
   //SPREMI
    public saveToSqlite(DataArrayTwo){
	  this.sqlite.create({
      name: 'baza.db',
      location: 'default'
      })
      .then((db: SQLiteObject) => {
           db.executeSql("INSERT INTO mojaDrugaTablica (firstname, lastname) VALUES (?,?)", [DataArrayTwo.firstname, DataArrayTwo.lastname]).then((data) => {
           console.log("INSERTED: " + JSON.stringify(data));
           }, (error) => {
           console.log("ERROR kod inserta: " + JSON.stringify(error.err));
        });
	  })
     .catch(e => console.log(e));
    }
    //DOHVATI
    public getMyOfflineData() {
        return new Promise((resolve, reject) => {
          this.sqlite.create({
          name: 'baza.db',
          location: 'default'
          })
          .then((db: SQLiteObject) => {
            db.executeSql("SELECT * FROM mojaDrugaTablica", []).then((data) => {
                let LocalArrayTwo = [];
                if(data.rows.length > 0) {
                    for(let i = 0; i < data.rows.length; i++) {
                        LocalArrayTwo.push({
                            firstname: data.rows.item(i).firstname,
                            lastname: data.rows.item(i).lastname
                        });
                    }
                }
                resolve(LocalArrayTwo);
            }, (error) => {
                reject(error);
            });
          })
         .catch(e => console.log(e));
        });
    }
    //OBRIŠI
    public deleteMyOfflineData(){
	  this.sqlite.create({
      name: 'baza.db',
      location: 'default'
      })
      .then((db: SQLiteObject) => {
           db.executeSql("DELETE FROM mojaDrugaTablica",[]).then((data) => {
           console.log("DELETED: " + JSON.stringify(data));
           }, (error) => {
           console.log("ERROR kod brisanja: " + JSON.stringify(error.err));
        });
	 })
     .catch(e => console.log(e));
    }

  }

Kako bi ovaj servis mogli koristiti trebamo ga navesti u app.module.ts

import { DatabaseService } from '../providers/my-data';
import { SQLite } from '@ionic-native/sqlite';
...
 providers: [StatusBar, 
             SplashScreen,
             DatabaseService, 
             SQLite, 
             {provide: ErrorHandler, useClass: IonicErrorHandler}]

Korištenje SQLite baze u ostatku aplikacije

Ranije kreiran SQLite servis sada možemo koristiti u ostatku aplikacije za spremanje podataka koje onda možemo prikazivati korisnicima kada aplikacije nije povezana s mrežom.

Recimo da u ovom primjeru na početnoj stranici želimo povući žive podatke s API-ja dok je aplikacije povezana s mrežom i odmah ih spremiti u SQLite bazu kako bi ih mogli koristiti kada se povezanost s mrežom izgubi.

Kada se otvori home.ts i pokrene funkcija storeMyOfflineData() pozvat će se funkcija getMyLiveDataFromApi() koja dovlači vanjske podatke u mobilnu aplikaciju. Te podatke u tom trenutku odmah možemo prikazati korisnicima. Istovremeno, pozivamo _database.saveTablicaToSqlite servis kojemu prosljeđujemo niz podataka this.DataArray[i] koje smo dobili iz API-ja.

import { Component } from '@angular/core';
import { NavController, IonicPage } from 'ionic-angular';
import { ApiProvider } from '../../providers/api-providers';
import { DatabaseService } from "../../providers/database-service";

@IonicPage()
@Component({
  selector: 'page-home',
  templateUrl: 'home.html',
  providers: [ApiProvider]
})
export class HomePage {

  public DataArray: Array<Object>;

  //Podaci potrebni API-ju da bi vratio podatke
  parameters = { user_id: localStorage.getItem('user_id'), access_token: localStorage.getItem('access_token') };

  constructor(public _navCtrl: NavController, 
              public _api: ApiProvider,
              public _database: DatabaseService) {
  }
  
   //Spremi žive podatke u lokalnu SQLite bazu
    storeMyOfflineData(){
         this._api.getMyLiveDataFromApi(this.parameters).subscribe(res => {
          this.DataArray = res;
          if(res.length > 0) {
                for(var i = 0; i < res.length; i++) {
                  this._database.saveTablicaToSqlite(this.DataArray[i]);
                }
          }
           }); 
    }
	
	//Prikaži lokalne podatke
	 getMyOfflineData(){
       this._database.getTablicaMyOfflineData().then((result) => {
            this.DataArray = <Array<Object>> result;
        }, (error) => {
            console.log("ERROR: ", error);
        });
    } 
	
	 //Isprazni lokalnu bazu
     deleteMyOfflineData() {
       this._database.deleteTablicaMyOfflineData();
    } 

}

Testiranje na mobilnom uređaju

Uspješnost kreiranja SQLite baze kao i korištenje najbolje ćete testirati na fizičkom uređaju. Spojite uređaj na PC, pokrenite naredbu

$ ionic run android

i otvorite chrome://inspect/#devices u Chrome web pregledniku. Ondje ćete vidjeti svoj mobilni uređaj na kojemu je pokrenuta aplikacija. Ispod imena aplikacije kliknite na inspect.

Sada u konzoli možete vidjeti da su SQLite tablice uspješno kreiranje.

Ionic 2 & 3 – SQLite servis - kreiranje tablice

Dalje, kada se otvori početna stranica može se vidjeti da se otvara baza i da se spremaju podaci. Sljedeći put kada opet otvorimo početnu stranicu, brišu se iz lokalne baze do tada spremljni podaci i dodaju se svježi podaci.

SQLite servis - brisanje i spremanje

Zaključak

Usporedite prethodni blog post i ovaj pa zaključite koji je način korištenja SQLite baze bolji. Moje mišljenje je da je bolji način korištenje servisa jer tako ne moramo ponavljati jedan te isti kod u ostatku aplikacije nego samo pozivamo funkcije za spremanje, čitanje i brisanje lokalnih podataka.

Stigao je Ionic 3!

Stigao nam je Ionic 3! Iako se iz samog naziva nove verzije promjena može činiti velikom zapravo nije, barem ne u onoj mjeri u kojoj je nakon Ionic verzije 1 stigla verzija 2.

Ako pokrećete novi projekt naredbom $ ionic start NazivAplikacije blank –v2 od petka, 07.04.2017., onda već koristite Ionic 3 i ne morate raditi izmjene koje ću opisati u nastavku.

Za sve vas koji imate postojeće projekte temeljene na Ionicu 2 i želite prijeći na Ionic 3 u nastavku možete saznati kako to napraviti.

Ionic 2 -> Ionic 3

Angular 4, koji je izašao prije nekoliko dana, jedan je od važnijih razloga zašto sada imamo i Ionic 3.

1.) package.json

Prije svega potrebno je obrisati postojeću node_modules mapu jer ćemo od sada koristiti nove NPM pakete.

Nakon toga, potrebno je navesti nove NPM pakete koji sada uključuju novi Angular 4 i novi TypeScript. To je moguće učiniti na način otvorite postojeću package.json datoteku i dodate sljedeće:

"dependencies": {
    "@angular/common": "4.0.0",
    "@angular/compiler": "4.0.0",
    "@angular/compiler-cli": "4.0.0",
    "@angular/core": "4.0.0",
    "@angular/forms": "4.0.0",
    "@angular/http": "4.0.0",
    "@angular/platform-browser": "4.0.0",
    "@angular/platform-browser-dynamic": "4.0.0",
    "@ionic-native/core": "3.4.2",
    "@ionic-native/splash-screen": "3.4.2",
    "@ionic-native/status-bar": "3.4.2",
    "@ionic/storage": "2.0.1",
    "ionic-angular": "3.0.1",
    "ionicons": "3.0.0",
    "rxjs": "5.1.1",
    "sw-toolbox": "3.4.0",
    "zone.js": "^0.8.4"
},
"devDependencies": {
  "@ionic/app-scripts": "1.3.0",
  "typescript": "~2.2.1"
}

VAŽNO!

Naravno, ako u postojećem Ionic 2 projektu imate neki NPM paket naveden pod dependencies ili devDependencies navedite i njega unutar package.json datoteke kako bi se i on ponovno instalirao jer vam u suprotnom Ionic 3 projekt neće raditi.

U jednom od svojih projekata koristio sam @ngx-translate/core NPM paket pa sam zato i njega naveo unutar package.json datoteke.

Sada možemo instalirati nove NPM pakete jednostavnom naredbom $ npm install

2.) BrowserModule

U app.module.ts je potrebno uvesti BrowserModule koji zapravo omogućava korištenje mogućnosti koje Angular pruža.

import { BrowserModule } from '@angular/platform-browser';
...
imports: [
  BrowserModule,
  IonicModule.forRoot(MyApp)
],

Ako koristite Http onda trebate uvesti i HttpModule

import { HttpModule } from '@angular/http';
...
imports: [
  BrowserModule,
  HttpModule,
  IonicModule.forRoot(MyApp)
],

3.) Ionic Native 3.x

Nekoliko dana prije nego što je stigao Ionic 3 dobili smo Ionic Native 3.x. To je zapravo novi način korištenja Cordova pluginova tj. Ionic Native pluginova. Više o tome možete saznati ovdje.

Uglavnom, u postojećoj Ionic 2 aplikaciji trebate opet instalirati sve pluginove tako da budu prilagođeni novoj strukturi ostatka aplikacije. Do sada ste pluginove instalirali na Ionic Native 2.x način, a to znači korištenje naredbe kao što je npr. $ ionic plugin add cordova-plugin-device te se onda taj plugin u aplikaciju poziva na sljedeći način import { Device } from ‘ionic-native’;

Ionic Native 3.x način instalacije i korištenja podrazumijeva instalaciju plugina još i s naredbom $ npm install –save @ionic-native/device pri čemu se uvoz plugina vrši na sljedeći način

import { Device } from '@ionic-native/device';

constructor(private device: Device) { }

...

console.log('Device UUID is: ' + this.device.uuid);

Znači, od sada svoje Ionic Native 3.x pluginove instalirate koristeći obje ranije navedene naredbe. Također obavezno provjerite dokumentaciju svakog plugina jer se vrlo lako može dogoditi da po novome koristi neke nove metode, a stare više ne i zbog toga vam se može dogoditi da aplikacija prikazuje grešku vezanu uz neki od pluginova.

4.) module.ts i IonicPage

Kada bi u Ionic 2 aplikaciji kreirali novu stranicu dobili biste tri datoteke (.html,.scss,.ts), dok ako idete kreirati novu stranicu u verziji 3 dobit ćete dodatnu datoteku s nastavkom .module.ts, a kako se ovdje radi o prebacivanju Ionic 2 aplikacije u Ionic 3 znači da svaka vaša ranije kreirana stranica treba dobiti svoju .module.ts datoteku.

U nastavku možete vidjeti primjer jedne takve datoteke

import { NgModule } from '@angular/core';
import { IonicPageModule } from 'ionic-angular';
import { HomeTabsPage } from './home-tabs';
 
@NgModule({
  declarations: [
    HomeTabsPage,
  ],
  imports: [
    IonicPageModule.forChild(HomeTabsPage),
  ],
  exports: [
    HomeTabsPage
  ]
})
export class HomeTabsPageModule {}

Nakon toga u .ts datoteci koju imate od ranije za sve stranice dodajte IonicPage dekorator na sljedeći način

import { IonicPage } from 'ionic-angular';
...
...
@IonicPage()
@Component({

5.) Uklanjanje uvoza stranica

Nakon prethodnog koraka možete proći kroz sve stranice tj. njihove .ts datoteke i maknuti uvoze drugih stranica import {AboutPage} from ‘../about/about’;. Isto napravite i unutar app.module.ts datoteke u kojoj ćete također maknuti stranice iz declarations i entryComponents nizova.

Način na koji ćete od sada međusobno povezivati stranice ne razlikuje se puno od “starog” načina osim što ćete naziv svake stranice proslijediti kao string.

Znači, umjesto let modal = this.modalCtrl.create(ReplacementModalPage); koristit ćete let modal = this.modalCtrl.create(‘ReplacementModalPage’); .

Mogući problemi

Ako ste napravili sve ranije navedeno vi od sada koristite novi Ionic 3 i Angular 4. Ipak, unatoč tome vaš Ionic 3 projekt i dalje ne mora raditi kako bi trebao, a uzroka može biti nekoliko.

ionic-native

Uncaught Error: Cannot find module "ionic-native" Ako dobijete grešku “Uncaught Error: Cannot find module “ionic-native”” znači da još negdje u aplikaciji koristite Ionic Native 2.x način uvoza pluginova. Kada iz svih datoteka izbacite import { NazivPlugina } from ‘ionic-native’; ovu grešku više nećete vidjeti.

forChild

Kreirali ste novu stranicu i dobili grešku Property ‘forChild’ does not exist on type ‘typeof IonicModule’ rješenje se da umjesto IonicModule koristite IonicPageModule .

ion-img

Ako dobijete grešku “TypeError: Cannot read property ‘isImgsUpdatable’ of null” to znači da unutar neke .html datoteke koristite <ion-img> tag, a koji se ne nalazi unutar <ion-content> taga. Više o tome ovdje.

Kako bi ovu grešku izbjegli koristite običan <img> tag na svim mjestima koja se ne nalaze unutar <ion-content> taga.

ionic grid

Sve ranije ste dobro napravili i gore navedenih grešaka više nema, ali aplikacija i dalje ne izgleda kako bi trebala i sve je nekako razbacano, nije na svojem mjestu,… Zašto je to tako? Odgovor se nalazi u novom Ionic Grid sistemu.

Npr.

<ion-col width-50>col</ion-col>

će od sada biti

<ion-col col-6>col</ion-col>

Više o tome u službenoj dokumentaciji.

Prođite kroz .html datoteke i napravite potrebne izmjene kako bi vaša nova Ionic 3 aplikacija zasjala u punom sjaju!

Zaključak

Kao što ste mogli vidjeti migracija na Ionic 3 može se obaviti relativno brzo i relativno jednostavno kada znate što treba napraviti. Ipak, prije nego objavite novu verziju aplikacije na Google Play Store ili Apple Store uložite nešto vremena u testiranje svih funkcionalnosti vaše Ionic 3 aplikacije pogotovo zato što ste instalirali nove verzije pluginova čije metode se mogu razlikovati od onih koje trenutno koristite u mobilnoj aplikaciji i koje je zbog toga potrebno prilagoditi.