U ovom ćemo blog postu napraviti jednostavnu Ionic 4 mobilnu aplikaciju koja će se sastojati od forme za unos, uređivanje, pregled i brisanje podataka tzv. CRUD aplikaciju.
Ovaj blog post će posebno biti zanimljiv za sve one koji tek žele postati developeri.
VAŽNO! Kako bi aplikacija mogla prikazivati, spremati, uređivati i brisati podatke potrebno je kreirati backend servis koristeći JSON Server.
Nekoliko je faza potrebno proći kako bi se došlo do funkcionalne Ionic 4 CRUD aplikacije, a to su:
- Instalirati NPM: $ npm install npm@latest -g
- Instalirati Ionic: $ npm install -g ionic
- kreiranje projekta
- kreiranje potrebnih ekrana (postavljanje formi za unos i uređivanje, postavljanje tablice za pregled popisa)
- kreiranje servisa za pozivanje vanjskih API-ja
Na kraju ćemo aplikaciju koristiti unutar web preglednika, u ovom slučaju to je Google Chrome, kroz Chrome DevTools.
Kreiranje Ionic 4 projekta
Prije nego kreiramo novi Ionic 4 projekt potvrdit ćemo da imamo instaliranu najnoviju verziju Ionic CLI-a, a to je u ovom trenutku 4.10.2.
Projekt kreiramo naredbom:
1 | $ ionic start Ionic4CRUD blank |
Iako u ovom primjeru koristimo blank predložak postoje još sidemenu i tabs, ali njima se trenutno nećemo baviti jer kreiraju više komponenti koje nam za izradu ovog primjera nisu bitne.
Nakon što je projekt uspješno kreiran možemo ga pokrenuti tj. vidjeti u web pregledniku na adresi http://localhost:8100/home, a to radimo naredbom:
1 | $ ionic serve |
Kreiranje ekrana za upravljanje podacima
Umjesto trenutnog sadržaja datoteke home.page.html
1 2 3 4 5 6 7 8 9 10 11 12 | <ion-header> <ion-toolbar> <ion-title> Ionic Blank </ion-title> </ion-toolbar> </ion-header> <ion-content padding> The world is your oyster. <p>If you get lost, the <a target="_blank" rel="noopener" href="https://ionicframework.com/docs/">docs</a> will be your guide.</p> </ion-content> |
dodajemo sljedeće:
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 56 57 58 59 60 61 62 63 64 | <ion-header> <ion-toolbar color="primary"> <ion-title> Ionic4 CRUD </ion-title> </ion-toolbar> </ion-header> <ion-content padding> <ion-card> <ion-row> <ion-col> <ion-item> <ion-input type="text" min="3" max="50" autocomplete="on" [(ngModel)]="imePrezime" [ngModelOptions]="{standalone: true}" placeholder="Ime i prezime"></ion-input> </ion-item> </ion-col> </ion-row> </ion-card> <ion-row> <ion-col text-center> <ion-button *ngIf="spremi" color="success" fill="solid" (click)="dodajOsobu(imePrezime)" [disabled]="!imePrezime || imePrezime.length < 3 || disabled">Spremi</ion-button> <ion-button *ngIf="!spremi" color="dark" fill="solid" (click)="urediOsobu(imePrezime)" [disabled]="!imePrezime || imePrezime.length < 3 || disabled">Uredi</ion-button> </ion-col> </ion-row> <ion-row padding-top> <ion-col> <ion-searchbar [(ngModel)]="searchTerm" [showCancelButton]="shouldShowCancel" animated=true (ngModelChange)="trazi()" placeholder="Traži"> </ion-searchbar> </ion-col> </ion-row> <ion-card> <ion-card-content> <ion-row> <ion-col col-11> <strong>Ime i prezime</strong> </ion-col> <ion-col col-1 text-right> <strong>Uredi/Obriši</strong> </ion-col> </ion-row> <ion-row *ngFor="let osoba of popisOsoba; let i = index"> <ion-col col-11> {{osoba.imePrezime}} </ion-col> <ion-col col-1 text-right> <ion-chip (click)="pokupiPodatke(osoba)"> <ion-label color="dark"><ion-icon name="create"></ion-icon></ion-label> </ion-chip> <ion-chip (click)="obrisiOsobu(osoba, i)"> <ion-label color="danger"><ion-icon name="trash"></ion-icon></ion-label> </ion-chip> </ion-col> </ion-row> </ion-card-content> </ion-card> </ion-content> |
Ekran sada izgleda ovako:
Kreiranje API servisa
Prije nego napravimo funkcionalni dio aplikacije koji će obavljati dodavanje, uređivanje, prikaz i brisanje podataka potrebno je kreirati servis zadužen za svu logiku vezanu uz korištenje vanjskih resursa. Za potrebe ovog primjera koristiti ćemo JSON Server.
1 | $ ionic generate service API |
Nakon kreiranja ovog servisa unutar datoteke api.service.ts može se vidjeti sljedeće:
1 2 3 4 5 6 7 8 9 | import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class APIService { constructor() { } } |
Servis se može koristiti za različite svrhe, ali kako će ovdje glavna svrha biti slanje upita na vanjski API znači da obavezno trebamo koristiti HttpClient i također ga trebamo dodati unutar app.module.ts datoteke.
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 | import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { RouteReuseStrategy } from '@angular/router'; import { IonicModule, IonicRouteStrategy } from '@ionic/angular'; import { SplashScreen } from '@ionic-native/splash-screen/ngx'; import { StatusBar } from '@ionic-native/status-bar/ngx'; import { AppComponent } from './app.component'; import { AppRoutingModule } from './app-routing.module'; import { HttpClientModule } from '@angular/common/http'; @NgModule({ declarations: [AppComponent], entryComponents: [], imports: [BrowserModule, IonicModule.forRoot(), AppRoutingModule, HttpClientModule], providers: [ StatusBar, SplashScreen, { provide: RouteReuseStrategy, useClass: IonicRouteStrategy } ], bootstrap: [AppComponent] }) export class AppModule {} |
Konačan izgled servisa može se vidjeti u nastavku, a njime je pokriveno sve – od pregleda svih osoba, dodavanja novih, uređivanja i brisanja postojećih.
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 | import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; @Injectable({ providedIn: 'root' }) export class APIService { constructor(private _http: HttpClient) { } //Popis osoba popisOsoba(){ return this._http.get('http://localhost:3000/osobe') } //Dodavanje osobe dodajOsobu(a:any){ return this._http.post('http://localhost:3000/osobe', a) } //Uređivanje osobe urediOsobu(a:any){ return this._http.put('http://localhost:3000/osobe/'+ a.id, a) } //Brisanje osobe obrisiOsobu(id:any){ return this._http.delete('http://localhost:3000/osobe/' + id) } } |
Funkcionalnost
Nakon uspješnog kreiranja izgleda ekrana i API servisa možemo sve to povezati kako bi naša Ionic 4 aplikacija “oživila”.
Na vrhu HomePage komponente prvo trebamo definirati naš ranije kreiran API servis.
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 | import { Component, OnInit } from '@angular/core'; import { AlertController } from '@ionic/angular'; import { APIService } from './../api.service'; @Component({ selector: 'app-home', templateUrl: 'home.page.html', styleUrls: ['home.page.scss'], }) export class HomePage implements OnInit { searchTerm: any; spremi: boolean = true; imePrezime: string; jednaOsoba = {id: 0, imePrezime: ''}; popisOsoba:any = []; popisOsoba2:any = []; disabled = false; constructor(private _alertCtrl: AlertController, private _apiService: APIService) { } ngOnInit() {} } |
Dodavanje (POST)
Funkcija za dodavanje nove osobe izgleda ovako:
1 2 3 4 5 6 7 | dodajOsobu(osoba){ console.log(osoba); this._apiService.dodajOsobu({"imePrezime":osoba}).subscribe(res => { console.log(res); }); this.disabled = true; } |
U gornjem primjeru nova osoba će biti kreirana i poslana na API. Međutim, nakon toga je potrebno osvježiti popis osoba na ekranu tako da odmah imamo povratnu informaciju o ne/uspješnom kreiranju nove osobe.
Imamo dvije mogućnosti prilikom osvježavanja podataka u tablici za prikaz osoba. Možemo odmah poznati API koji će nam dovući popis svih osoba i tako osvježiti tablicu
1 2 3 4 5 6 7 8 9 10 11 12 | dodajOsobu(osoba){ console.log(osoba); this._apiService.dodajOsobu({"imePrezime":osoba}).subscribe(res => { //Pozivanje API-ja sa popisom svih osoba if(res){ this.imePrezime = ""; this.disabled = false; this.dohvatiPopisOsoba(); } }); this.disabled = true; } |
ili možemo “ručno” dodati novi podatak u postojeći niz i na taj način ne pozivati API koji dohvaća popis svih osoba.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | dodajOsobu(osoba){ console.log(osoba); this._apiService.dodajOsobu({"imePrezime":osoba}).subscribe(res => { //Pozivanje API-ja sa popisom svih osoba if(res){ this.imePrezime = ""; this.disabled = false; this.popisOsoba.push({ id: res['id'], imePrezime: res['imePrezime'] }); } }); this.disabled = true; } |
Što god od toga dvoje odabrali rezultat će biti jednak tj. postići ćemo da nam se podataka o novoj osobi odmah prikaže u našoj tablici.
Dohvaćanje i prikaz (GET)
Dohvaćanje tj. prikaz podataka vršimo kroz ngOnInit tj. dohvatiPopisOsoba() funkciju.
1 2 3 4 5 6 7 8 9 10 11 | ngOnInit() { this.dohvatiPopisOsoba(); } dohvatiPopisOsoba(){ this._apiService.popisOsoba().subscribe(res => { console.log(res); this.popisOsoba = res; this.popisOsoba2 = res; }); } |
U praksi to izgleda ovako:
Osim toga, dodali smo i jednostavnu tražilicu:
1 2 3 4 5 6 7 8 9 10 11 12 13 | trazi(){ let term = this.searchTerm; if(term.length >= 2){ this.popisOsoba = this.popisOsoba2.filter((korisnik) => { if((("" + korisnik.imePrezime).toLowerCase()).includes(term.toLowerCase())){ return true; } return false; }); }else{ this.popisOsoba = this.popisOsoba2; } } |
Uređivanje (PUT)
Funkcija za uređivanje jedne osobe dijeli se u dva dijela. Prvi prikuplja podatke jedne osobe, a drugi ih šalje na API tj. izvršava promjenu u vanjskoj bazi.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | pokupiPodatke(osoba){ this.spremi = false; console.log(osoba); this.jednaOsoba.id = osoba.id; this.jednaOsoba.imePrezime = osoba.imePrezime; this.imePrezime = osoba.imePrezime; } urediOsobu(imePrezime){ this.spremi = true; this.imePrezime = imePrezime; console.log(this.jednaOsoba.id); console.log(this.imePrezime); this._apiService.urediOsobu({"id": this.jednaOsoba.id, "imePrezime":imePrezime}).subscribe(res => { console.log(res); if(res){ this.imePrezime = ""; this.disabled = false; this.dohvatiPopisOsoba(); } }); } |
Na kraju to izgleda ovako:
Brisanje (DELETE)
Kada je brisanje u pitanju trebamo biti posebno pažljivi i korisnik uvijek mora potvrditi da stvarno želi nešto obrisati.
Za potvrdu ćemo koristiti AlertController.
Nakon što je osoba uspješno obrisana opet imamo dvije mogućnosti kada je u pitanju osvježavanje podataka s popisom osoba. Možemo opet pozvati API koji će dovući popis svih osoba ili možemo koristiti .splice() metodu i “ručno” ukloniti pojedini podataka prema njegovom mjestu u nizu bez da istovremeno pozivamo API.
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 | brisanjeOsobe(osoba,index){ console.log(osoba); this._apiService.obrisiOsobu(osoba.id).subscribe(res => { console.log(res); if(res){ //"Ručno" uklanjanje iz postojećeg niza, bez pozivanja API-ja this.popisOsoba.splice(index, 1); //Pozivanje API-ja //this.dohvatiPopisOsoba(); } }); } async obrisiOsobu(osoba,index) { const alert = await this._alertCtrl.create({ header: osoba.name, message: 'Jeste li sigurni da želite obrisati ovu osobu?', buttons: [ { text: 'Odustani', role: 'cancel', cssClass: 'secondary', handler: () => { // } }, { text: 'Da', handler: () => { this.brisanjeOsobe(osoba,index); } } ] }); await alert.present(); } |
U praksi to izgleda ovako:
Kompletna funkcionalnost na kraju izgleda ovako:
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 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | import { Component, OnInit } from '@angular/core'; import { AlertController } from '@ionic/angular'; import { APIService } from './../api.service'; @Component({ selector: 'app-home', templateUrl: 'home.page.html', styleUrls: ['home.page.scss'], }) export class HomePage implements OnInit { searchTerm: any; spremi: boolean = true; imePrezime: string; jednaOsoba = {id: 0, imePrezime: ''}; popisOsoba:any = []; popisOsoba2:any = []; disabled = false; constructor(private _alertCtrl: AlertController, private _apiService: APIService) { } ngOnInit() { this.dohvatiPopisOsoba(); } dohvatiPopisOsoba(){ this._apiService.popisOsoba().subscribe(res => { console.log(res); this.popisOsoba = res; this.popisOsoba2 = res; }); } dodajOsobu(osoba){ console.log(osoba); this._apiService.dodajOsobu({"imePrezime":osoba}).subscribe(res => { //Pozivanje API-ja sa popisom svih osoba if(res){ this.imePrezime = ""; this.disabled = false; this.popisOsoba.push({ id: res['id'], imePrezime: res['imePrezime'] }); } }); this.disabled = true; } trazi(){ let term = this.searchTerm; if(term.length >= 2){ this.popisOsoba = this.popisOsoba2.filter((korisnik) => { if((("" + korisnik.imePrezime).toLowerCase()).includes(term.toLowerCase())){ return true; } return false; }); }else{ this.popisOsoba = this.popisOsoba2; } } pokupiPodatke(osoba){ this.spremi = false; console.log(osoba); this.jednaOsoba.id = osoba.id; this.jednaOsoba.imePrezime = osoba.imePrezime; this.imePrezime = osoba.imePrezime; } urediOsobu(imePrezime){ this.spremi = true; this.imePrezime = imePrezime; console.log(this.jednaOsoba.id); console.log(this.imePrezime); this._apiService.urediOsobu({"id": this.jednaOsoba.id, "imePrezime":imePrezime}).subscribe(res => { console.log(res); if(res){ this.imePrezime = ""; this.disabled = false; this.dohvatiPopisOsoba(); } }); } brisanjeOsobe(osoba,index){ console.log(osoba); this._apiService.obrisiOsobu(osoba.id).subscribe(res => { console.log(res); if(res){ //"Ručno" uklanjanje iz postojećeg niza, bez pozivanja API-ja this.popisOsoba.splice(index, 1); //Pozivanje API-ja //this.dohvatiPopisOsoba(); } }); } async obrisiOsobu(osoba,index) { const alert = await this._alertCtrl.create({ header: osoba.name, message: 'Jeste li sigurni da želite obrisati ovu osobu?', buttons: [ { text: 'Odustani', role: 'cancel', cssClass: 'secondary', handler: () => { // } }, { text: 'Da', handler: () => { this.brisanjeOsobe(osoba,index); } } ] }); await alert.present(); } } |
Zaključak
Ovo je bio jednostavan primjer kreiranja Ionic 4 mobilne aplikacije sa osnovnim funkcionalnostima kao što su prikaz, dodavanje, uređivanje i brisanje podataka.
Struktura projekta prema 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 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | { "name": "Ionic4CRUD", "version": "0.0.1", "author": "Tomislav Stanković", "homepage": "https://www.tomislavstankovic.com/", "scripts": { "ng": "ng", "start": "ng serve", "build": "ng build", "test": "ng test", "lint": "ng lint", "e2e": "ng e2e" }, "private": true, "dependencies": { "@angular/common": "^7.2.2", "@angular/core": "^7.2.2", "@angular/forms": "^7.2.2", "@angular/http": "^7.2.2", "@angular/platform-browser": "^7.2.2", "@angular/platform-browser-dynamic": "^7.2.2", "@angular/router": "^7.2.2", "@ionic-native/core": "^5.0.0", "@ionic-native/splash-screen": "^5.0.0", "@ionic-native/status-bar": "^5.0.0", "@ionic/angular": "^4.0.0", "core-js": "^2.5.4", "rxjs": "~6.3.3", "zone.js": "~0.8.29" }, "devDependencies": { "@angular-devkit/architect": "~0.12.3", "@angular-devkit/build-angular": "~0.12.3", "@angular-devkit/core": "~7.2.3", "@angular-devkit/schematics": "~7.2.3", "@angular/cli": "~7.2.3", "@angular/compiler": "~7.2.2", "@angular/compiler-cli": "~7.2.2", "@angular/language-service": "~7.2.2", "@ionic/angular-toolkit": "~1.3.0", "@types/node": "~10.12.0", "@types/jasmine": "~2.8.8", "@types/jasminewd2": "~2.0.3", "codelyzer": "~4.5.0", "jasmine-core": "~2.99.1", "jasmine-spec-reporter": "~4.2.1", "karma": "~3.1.4", "karma-chrome-launcher": "~2.2.0", "karma-coverage-istanbul-reporter": "~2.0.1", "karma-jasmine": "~1.1.2", "karma-jasmine-html-reporter": "^0.2.2", "protractor": "~5.4.0", "ts-node": "~8.0.0", "tslint": "~5.12.0", "typescript": "~3.1.6" }, "description": "Ionic4CRUD" } |