Gore se može vidjeti običan servis koji služi za pozivanje API-ja s popisom država. API će se pozvati isključivo nakon određene akcije korisnika. To može biti npr. na klik, prilikom otvaranja neke stranice unutar aplikacije ili na neki treći način.
Međutim, što napraviti ako želim u određenom vremenskom intervalu ponovno pozvati API kako bi se podaci osvježili bez da to korisnik mora učiniti?
U tom slučaju unutar servisa mogu definirati
Observable.timer(x,x) koji prima dva parametra. Prvi parameter označava broj milisekundi nakon kojih će se API pozvati, a drugi označava svakih koliko milisekundi će se API ponovno pozvati.
U ovom slučaju API će prvi put dovući podatke bez odgode, a kasnije će to činiti svakih 6 sekundi. Posebno zanimljiva je i činjenica da će se API nastaviti pozivati čak i u slučaju kada aplikacije nije pokrenuta.
Jedna od osnovnih funkcionalnosti koju volim vidjeti u mobilnim aplikacijama je mogućnost rada izvan mreže (offline) tj. kada mobilni uređaj nije povezan s internetom.
Pretpostavimo da aplikacija ima nekakvu formu za unos koju je potrebno ispuniti i da se klikom na gumb ‘Pošalji podatke’ ništa ne događa. Možda tek eventualno bude nekakva poruka da trenutno nema mrežne povezanosti i zato nije moguće spremiti unesene podatke. Najgora stvar koja se onda može dogoditi je da aplikacija ne zapamti unesene podatke i da ih korisnik kasnije mora ponovno upisivati.
U ovom ću blog postu obraditi scenarij mobilne aplikacija koja ima nekakvu formu za unos koju je moguće ispuniti i spremiti podatke neovisno i povezanosti s internetom. Korisniku je najbitnije da ne mora više puta unositi podatke ili razmišljati zašto ih nije u mogućnosti spremiti.
Napravim servis npr. network-service.ts kojemu će jedina i osnovna svrha biti provjera povezanosti s internetom. Dalje, na svakoj stranici mobilne aplikacije pozivam taj servis ili prilikom klika na gumb ‘Pošalji podatke’ i ako dobijem informaciju da nema povezanosti s internetom automatski znam da nešto drugo s tim podacima trebam učiniti, a to je – spremiti ih u lokalnu bazu podataka.
JavaScript
1
2
3
4
5
6
7
8
9
provjeraInterneta(){
if(this._networkService.isOnline()){
console.log('Ima interneta!');
//Odmah šalji podatke na API
}else{
console.log('Nema interneta!');
//Spremi podatke u SQLite bazu
}
}
Spremanje podataka u lokalnu SQLite bazu
O ovoj sam temi također objavio blog post. Kada sam u prethodnom koraku dobio informaciju da ne postoji povezanost s internetom odlučujem se na spremanje podataka lokalno na mobilni uređaj. Kako bi to mogao učiniti moram prvo kreirati lokalnu bazu podataka, a to činim koristeći Ionic Native SQLite plugin.
Najbolje je opet napraviti poseban servis, npr. database-service.ts, koji će služiti za sve radnje (kreiranje baze, kreiranje tablica, spremanje, prikaz i brisanje podataka) povezane sa SQLite bazom.
Klikom na gumb ‘Pošalji podatke’ sada se događa sljedeće. U SQLite tablicu
mojaLokalnaTablica spremam podatke unesene u formu. Podaci su u obliku objekta
this.podaciIzForme.
Na ovaj način korisnik može bez problema unositi podatke neovisno o povezanosti s internetom jer će se svi podaci spremiti u lokalnu SQLite bazu podataka koja se nalazi na mobilnom uređaju.
Korisno je dati korisniku i nekakvu obavijest o ne postojanju povezanosti s internetom čisto iz razloga ako se odluči npr. otići na računalo i u nekakvoj web aplikaciji provjeriti podatke koje je maloprije poslao, da se ne iznenadi ako ih u tom trenutku ne vidi. Bitno je da su podaci spremljeni i da će biti poslani čim se ostvari povezanost s internetom.
Automatsko slanje podataka / sinkronizacija
Sada dolazim do dijela koji je u ovoj priči najvažniji. Znači, korisnik je bez problema koristio mobilnu aplikaciju i nekoliko puta unosio različite podatke u formu za unos i svi ti podaci su privremeno spremljeni u lokalnu SQLite bazu podataka.
U ovom je koraku ključno prepoznati trenutak kada se ponovno uspostavi povezanost s internetom i sve ranije spremljene podatke poslati na API te istovremeno isprazniti lokalnu bazu podataka.
Na početnoj stranici mobilne aplikacije mogu pozvati funkciju
provjeraInterneta() koja će svaki put kada se dođe na početnu stranicu provjeravati postoji li povezanost s internetom i ako postoji odmah će poslati sve lokalne podatke te isprazniti lokalnu bazu.
JavaScript
1
2
3
4
5
6
7
8
provjeraInterneta(){
if(this._networkService.isOnline()){
console.log('Ima interneta!');
this.posaljiLokalnePodatke();
}else{
console.log('Ne šaljem podatke! Nema interneta!');
}
}
Sada na početnoj stranici radim sljedeće. Pozivam servis koji provjerava povezanost s internetom. Ako postoji veza s internetom pozivam funkciju
posaljiLokalnePodatke(). Ta funkcija dohvaća niz (array) svih objekata koji su spremljeni u lokalnu SQLite bazu i šaljem ih na API. Kada stigne odgovor od API-ja da su podaci uspješno primljeni krećem s brisanjem lokalne baze podataka
this._database.isprazniLokalnuTablicu().
S obzirom da za nekoliko sati stiže Božić potrudit ću se ovaj blog post učiniti što konkretnijim bez puno okolišanja. U nastavku ću pokazati kako u Ionic aplikaciju dodati podršku za vibraciju.
Vibracija je jedna od onih funkcionalnosti koja nekoj mobilnoj aplikaciji može dati završni sjaj ili ju učiniti odbojnom korisnicima i zato treba ju treba pažljivo koristiti.
U ovom ću blog postu koristiti Google Maps Directions API za prikaz rute od jedne do druge lokacije. Također, ovaj će blog post biti vrlo sličan jednom kojeg sam objavio ranije ove godine pod naslovom “Ionic 3 – Prikaz Google mape“.
Aplikacija sada nema nikakve funkcionalnosti pa ću prije nego implementiram Google Maps Directions API implementirati Lazy Loading. Ovaj korak nije potreban i može se bez njega, ali će dobro za kasnije brže učitavanje aplikacije.
Prvi korak koji je potrebno učiniti tiče se stranice ‘src/app/app.module.ts’ u kojoj je potrebno ukloniti sve poveznice na Home stranicu.
Nakon toga kreiram novu stranicu ‘src/pages/home/home.module.ts’ sa sljedećim sadržajem
home.module.ts
JavaScript
1
2
3
4
5
6
7
8
9
10
import{NgModule}from'@angular/core';
import{HomePage}from'./home';
import{IonicPageModule}from'ionic-angular';
@NgModule({
declarations:[HomePage],
imports:[IonicPageModule.forChild(HomePage)],
entryComponents:[HomePage]
})
exportclassHomePageModule{}
Sada se vraćam na stranicu ‘src/pages/home/home.ts’ unutar koje moram dodati IonicPage pa to sada izgleda ovako
home.ts
JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import{IonicPage}from'ionic-angular';
import{Component}from'@angular/core';
import{NavController}from'ionic-angular';
@IonicPage()
@Component({
selector:'page-home',
templateUrl:'home.html'
})
exportclassHomePage{
constructor(public navCtrl:NavController){
}
}
I za kraj odlazim u ‘src/app/app.component.ts’ gdje uklanjam import na HomePage stranicu i stavljam ju pod navodnike jer tako funkcionira Lazy Loading.
Electron je framework za razvoj cross-platform desktop aplikacija koristeći JavaScript, HTML i CSS. S tehnologijama koje koristi jako podsjeća na Ionic, a omogućava razvoj desktop aplikacija koje se mogu pokrenuti na Windows, Mac i Linux platformi.
Priprema Ionic projetka
Za početak ćemo kreirati novi Ionic projekt.
Shell
1
2
3
$ionic start Ionic3ElectronApp sidemenu
$cdIonic3ElectronApp
$ionic serve
Kada aplikaciju pokrenemo nećemo vidjeti ništa što već ranije nismo vidjeli. Standardna Ionic aplikacija koja se pokreće u web pregledniku kao što je npr. Google Chrome.
Postavljanje Electron projekta
Sada je potrebno dodati Electron unutar našeg Ionic projekta koristeći naredbu:
Sada ćemo kreirati datoteku main.js koja će biti ulazna točka prilikom pokretanja Ionic-Electron aplikacije. Više o ovoj datoteci možete pronaći na adresi https://electron.atom.io/docs/tutorial/quick-start/
main.js
JavaScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
const{app,BrowserWindow}=require('electron')
constpath=require('path')
consturl=require('url')
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let win
functioncreateWindow(){
// Create the browser window.
win=newBrowserWindow({width:800,height:600})
// and load the index.html of the app.
win.loadURL(url.format({
pathname:path.join(__dirname,'www/index.html'),
protocol:'file:',
slashes:true
}))
// Open the DevTools.
win.webContents.openDevTools()
// Emitted when the window is closed.
win.on('closed',()=>{
// Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
win=null
})
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready',createWindow)
// Quit when all windows are closed.
app.on('window-all-closed',()=>{
// On macOS it is common for applications and their menu bar
// to stay active until the user quits explicitly with Cmd + Q
if(process.platform!=='darwin'){
app.quit()
}
})
app.on('activate',()=>{
// On macOS it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if(win===null){
createWindow()
}
})
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.
Osim te datoteke moramo unutar package.json dodati dvije važne naredbe.
"main":"main.js" služi kako bi se znalo koju datoteku gledati kada se projekt pokrene kao Electron aplikacija.
"start":"electron ." služi kao naredba za pokretanje Electron aplikacije.
package.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
{
"name":"Ionic3ElectronApp",
"version":"0.0.1",
"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",
"start":"electron ."
},
"dependencies":{
"@angular/common":"4.4.4",
"@angular/compiler":"4.4.4",
"@angular/compiler-cli":"4.4.4",
"@angular/core":"4.4.4",
"@angular/forms":"4.4.4",
"@angular/http":"4.4.4",
"@angular/platform-browser":"4.4.4",
"@angular/platform-browser-dynamic":"4.4.4",
"@ionic-native/core":"4.3.2",
"@ionic-native/splash-screen":"4.3.2",
"@ionic-native/status-bar":"4.3.2",
"@ionic/storage":"2.0.1",
"electron":"^1.7.9",
"ionic-angular":"3.8.0",
"ionicons":"3.0.0",
"rxjs":"5.4.3",
"sw-toolbox":"3.6.0",
"zone.js":"0.8.18"
},
"devDependencies":{
"@ionic/app-scripts":"3.0.1",
"typescript":"2.3.4"
},
"description":"An Ionic project",
"main":"main.js"
}
Spremni smo za kreiranje skripte buildElectron.sh koja će obaviti nekoliko radnji automatski tako da ih ne moramo svaki put ručno pokretati.
buildElectron.sh
Shell
1
2
3
4
ionic cordova build browser--prod
rm-rf www/*
cp-rplatforms/browser/www/*www/
npm start
I konačno, Ionic-Electron aplikaciju možemo pokrenuti naredbom
./buildElectron.sh
ili dvoklikom na ikonu buildElectron skripte unutar našeg projekta.
nakon čega dobijemo našu Ionic aplikaciju unutar Electrona.
Možete primijetiti da se prilikom pokretanja aplikacije automatski pokrenuo i Developer Tools. To možemo spriječiti ako unutar main.js datoteke zakomentiramo
win.webContents.openDevTools().
Dizajn i prilagodba sučelja
Dizajn trenutno kreirane aplikacije prilagođen je mobilnim uređajima i ako ju raširimo preko cijelog ekrana nekog desktop uređaja to neće izgledati lijepo jer će npr. popis stavki ići jedan ispod drugog preko cijelog ekrana umjesto da se fino rasporedi po nekoliko stavki u jedan red.
I onda se proces kreiranja produkcijske verzije Electron desktop aplikacije pokreće sljedećim naredbama:
– Windows OS:
npm run package-win
– Mac:
npm run package-mac
– Linux:
npm run package-linux
Zaključak
Iz ovog ste blog posta mogli vidjeti kako Ionic mobilnu aplikaciju pretvoriti u cross-platform destkop aplikaciju pa iako se to čini zanimljivim i očito da je moguće osobno se ne planiram detaljnije tome posvetiti. Također, ne treba zanemariti niti činjenicu da na tu temu ne postoji dovoljno primjera i dokumentacije.