U ovom ću blog postu napraviti jednostavnu Angular CRUD (create, read, update, delete) aplikaciju koja će se sastojati od forme za pregled podataka uz mogućnost dodavanja, uređivanja i brisanja podataka.
CRUD operacije nešto su s čime se svaki web developer najčešće susreće i zato bi ovaj primjer posebno mogao biti koristan novim developerima ili onima koji će to tek postati.
Uvod
Cilj ovog blog posta je pokazati:
– kako kreirati Angular projekt koristeći Angular CLI
– kako kreirati i koristiti servis pomoću kojega ću pozivati JSON API
– kako dodati Bootstrap za brže kreiranje forme za unos, pregled, uređivanje i brisanje sadržaja
Kreiranje Angular projekta
Pomoću Angular CLI-a kreirat ću novi projekt.
1 2 |
$ ng new AngularCRUD $ ng serve |
Ako projekt sada otvorim u web pregledniku na adresi http://localhost:4200/ dobit ću sljedeće
S obzirom da umjesto toga želim imati formu za unos, pregled, uređivanje i brisanje sadržaja prvo ću dodati podršku za Bootstrap na način da u index.html dodam
1 |
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"> |
. I sada mogu koristiti Bootstrap komponente.
Kasnije ću se vratiti na izgled forme, a sada idem postaviti glavni dio funkcionalnosti koji se tiče kreiranja servisa i pozivanja API-ja.
Kreiranje servisa za API
Servis pomoću kojega ću vršiti sve upite na API kreiram naredbom
1 |
$ ng generate service KorisniciAPI |
Nakon čega dobijem korisnici-api.service.ts
1 2 3 4 5 6 7 8 |
import { Injectable } from '@angular/core'; @Injectable() export class KorisniciApiService { constructor() { } } |
Servis se može koristiti za različite svrhe, ali kako ću meni ovdje glavna svrha biti slanje upita na API znači da obavezno moram koristiti Http servis.
Konačan izgled servisa može se vidjeti u nastavku, a njime je pokriveno sve – od pregleda svih korisnika, 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 33 34 |
import { Injectable } from '@angular/core'; import { Http } from '@angular/http'; import 'rxjs/add/operator/map'; @Injectable() export class KorisniciApiService { constructor(private _http: Http) {} // Pregled svih korisnika u imeniku svi(){ return this._http.get('http://localhost:3000/Imenik') .map(res => res.json()); } // Dodavanje korisnika u imenik dodaj(a:any){ return this._http.post('http://localhost:3000/Imenik', a) .map(res => res.json()); } // Uređivanje korisnika u imeniku uredi(a:any){ return this._http.put('http://localhost:3000/Imenik/' + a.id, a) .map(res => res.json()); } // Brisanje korisnika iz imenika obrisi(a:any){ return this._http.delete('http://localhost:3000/Imenik/' + a) .map(res => res.json()); } } |
Izgled forme (app.component.html)
Za izgled forme iskoristit ću Bootstrap komponente. Struktura forme nalazi se unutar app.component.html, a 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 |
<div class="container-fluid"> <div class="row"> <div class="col-lg-8"> <div class="panel panel-default"> <div class="panel-heading">Dodaj/Uredi korisnika:</div> <div class="panel-body"> <form name="ImenikForma" novalidate> <div class="form-group"> <div class="row"> <div class="col col-lg-5 col-md-5" ng-class="{ 'has-error' : ImenikForma.ime.$invalid && !ImenikForma.ime.$pristine }"> <div id="input_container"> <input type="text" class="form-control" id="ime" required maxlength="30" placeholder="Ime i prezime" [(ngModel)]="korisnik.ime" [ngModelOptions]="{standalone: true}" #ime="ngModel"> </div> </div> <div class="col col-lg-5 col-md-5" ng-class="{ 'has-error' : ImenikForma.telefon.$invalid && !ImenikForma.telefon.$pristine }"> <div id="input_container"> <input type="tel" class="form-control" id="telefon" required placeholder="Telefon" [(ngModel)]="korisnik.telefon" [ngModelOptions]="{standalone: true}" #telefon="ngModel"> </div> </div> <div class="col col-lg-2 col-md-2"> <div id="input_container"> <div *ngIf="spremi" class="text-center"><button type="button" class="btn btn-success btn-md" (click)="dodajKorisnika(korisnik)" [disabled]="!ime.valid || !telefon.valid"> Spremi </button></div> <div *ngIf="!spremi" class="text-center"><button type="button" class="btn btn-success btn-md" (click)="urediKorisnika2(korisnik)" [disabled]="!ime.valid || !telefon.valid"> Uredi </button></div> </div> </div> </div> </div> </form> </div> </div> </div> </div> <div class="row"> <div class="col-lg-8"> <div class="panel panel-default"> <div class="panel-heading">Ukupno korisnika: {{ukupanbrojkorisnika}}</div> <div class="panel-body"> <form novalidate> <div > <span *ngIf="ukupanbrojkorisnika < 1">Nema podataka</span> <table *ngIf="ukupanbrojkorisnika > 0" class="table table-striped table-hover table-responsive" ng-switch-default> <thead> <tr> <th class="tablica text-center">Rb.</th> <th class="tablica text-center">Ime</th> <th class="tablica text-center">Telefon</th> <th class="tablica text-center">Uredi | Obriši</th> </tr> </thead> <tbody> <tr *ngFor="let kor of svikorisnici; let i = index"> <td class="tablica text-center">{{i+1}}.</td> <td class="tablica text-center">{{kor.ime}}</td> <td class="tablica text-center">{{kor.telefon}}</td> <td class="tablica text-center"> <button class="btn btn-info btn-sm" (click)="urediKorisnika(kor)"><i class="glyphicon glyphicon-edit"></i></button> <button class="btn btn-danger btn-sm" (click)="obrisiKorisnika(kor)"><i class="glyphicon glyphicon-trash"></i></button> </td> </tr> </tbody> </table> </div> </form> </div> </div> </div> </div> </div> |
Forma na početku i bez podataka izgleda ovako:
Sada kada imam definiran izgled forme mogu kreirati opcije koje će joj dati funkcionalnost.
Dodavanje (POST)
Što se dodavanja sadržaja tiče nije ga moguće dodati dok sva obavezna polja nisu unesena. U ovom slučaju postoje dva polja, ime i prezime te broj telefona, i oba su obavezna. S obzirom da u formi za sada nema niti jednog korisnika *ngIf="ukupanbrojkorisnika < 1" prikezuje se poruka “Nema podataka”.
1 2 3 4 5 6 7 8 9 |
dodajKorisnika(korisnik){ this._korisniciService.dodaj(korisnik).subscribe( res => { console.log(res); this.dohvatiSveKorisnike(); this.korisnik.ime = ' '; this.korisnik.telefon = ' '; }); } |
Nakon dodavanja korisnika popis se automatski osvježava i prikazuje sve dodane korisnike.
Pregled (GET)
Kada se Angular web aplikacija pokrene automatski se dohvaća popis svih do tada unesenih korisnika. Za tu svrhu koristim ngOnInit() što je dio Angular Lifecycle Hook.
1 2 3 4 5 6 7 8 9 10 11 |
ngOnInit() { this.dohvatiSveKorisnike(); } dohvatiSveKorisnike(){ this._korisniciService.svi().subscribe(res => { console.log(res); this.svikorisnici = res; this.ukupanbrojkorisnika = res.length; }); } |
Također, odmah dohvaćam i dužinu niza this.ukupanbrojkorisnika = res.length; kako bi mogao prikazati ukupan broj korisnika.
Uređivanje (PUT)
Kod uređivanja korisnika imam dvije funkcije. Prva urediKorisnika(); dohvaća podatke odabranog korisnika i prikazuje ih u gornjoj formi za unos/uređivanje, a druga urediKorisnika2(); obavlja proces uređivanja odabranog korisnika tj. šalje nove podatke na API.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
urediKorisnika(kor){ console.log(kor); this.spremi = false; this.korisnik.id = kor.id; this.korisnik.ime = kor.ime; this.korisnik.telefon = kor.telefon; } urediKorisnika2(korisnik){ console.log(korisnik); this._korisniciService.uredi(korisnik).subscribe( res => { console.log(res); this.dohvatiSveKorisnike(); this.korisnik.ime = ' '; this.korisnik.telefon = ' '; this.spremi = true; }); } |
Nakon što su novi podaci poslani popis korisnika se automatski osvježava, a gumb ‘Uredi’ ponovno postaje ‘Spremi’.
Brisanje (DELETE)
Brisanje korisnika zadnji je korak izrade ovog CRUD primjera. Nakon što je odabrani korisnik obrisan popis se automatski osvježava.
1 2 3 4 5 6 7 8 9 10 |
obrisiKorisnika(kor){ let id = kor.id; if(window.confirm('Jeste li sigurni da želite obrisati korisnika?')){ this._korisniciService.obrisi(id).subscribe( res => { console.log(res); this.dohvatiSveKorisnike(); }); } } |
Zaključak
Ovo je bio jednostavan primjer Angular CRUD forme. Mogao sam ovdje dodatno uljepšati izgled forme ili na drugačiji način slati upite na API, ali mislim da je ovo sasvim dovoljno za početak jer kako god bilo korištenjem ovog primjera može se napraviti funkcionalna forma za unos, pregled, uređivanje i brisanje sadržaja.
I za kraj, NPM paketi korišteni u ovom primjeru:
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 |
{ "name": "angular-crud", "version": "0.0.0", "license": "MIT", "scripts": { "ng": "ng", "start": "ng serve", "build": "ng build", "test": "ng test", "lint": "ng lint", "e2e": "ng e2e" }, "private": true, "dependencies": { "@angular/animations": "^4.2.4", "@angular/common": "^4.2.4", "@angular/compiler": "^4.2.4", "@angular/core": "^4.2.4", "@angular/forms": "^4.2.4", "@angular/http": "^4.2.4", "@angular/platform-browser": "^4.2.4", "@angular/platform-browser-dynamic": "^4.2.4", "@angular/router": "^4.2.4", "core-js": "^2.4.1", "rxjs": "^5.4.2", "zone.js": "^0.8.14" }, "devDependencies": { "@angular/cli": "1.4.7", "@angular/compiler-cli": "^4.2.4", "@angular/language-service": "^4.2.4", "@types/jasmine": "~2.5.53", "@types/jasminewd2": "~2.0.2", "@types/node": "~6.0.60", "codelyzer": "~3.2.0", "jasmine-core": "~2.6.2", "jasmine-spec-reporter": "~4.1.0", "karma": "~1.7.0", "karma-chrome-launcher": "~2.1.1", "karma-cli": "~1.0.1", "karma-coverage-istanbul-reporter": "^1.2.1", "karma-jasmine": "~1.1.0", "karma-jasmine-html-reporter": "^0.2.2", "protractor": "~5.1.2", "ts-node": "~3.2.0", "tslint": "~5.7.0", "typescript": "~2.3.3" } } |
Pozdrav,
u skripti korisnici-api.service.ts exporta se klasa
export class KorisniciApiService
a nadalje se u primjerima poziva
this._korisniciService.uredi(korisnik),
this._korisniciService.obrisi(id)
Da li je to ispravan poziv ili se mora pozivati
this._KorisniciApiService.uredi(korisnik),
?this._KorisniciApiService.obrisi(id)
Ako ne, gdje se definira ovaj poziv
this._korisniciService.uredi
?Hvala
Pozdrav Jadranko,
this._korisniciService
se definira u konstruktoru na sljedeći način:import { KorisniciApiService } from from './korisnici-api.service';
constructor(public _korisniciService: KorisniciApiService) {}
i onda se koristi kako je gore u primjeru navedeno. Moja je greška što to nisam ubacio u blog post.
Pozdrav, interesira me kamo idu, odnosno gdje se spremaju CRUD funkcije dodajKorisnika, dohvatiSveKorisnike, urediKorisnika, urediKorisnika2 i obrisiKorisnika. Pretpostavljam da treba još dodatni servis napraviti po imenom korisnici.
Pozdrav Darko,
sve navedene funkcije nalaze se u app.component.ts. Servis za upravljanje korisnicima postoji pod imenom KorisniciApiService.
Kako bi se podaci spremali potrebno je napraviti servis i bazu putem kojih će se upravljati podacima. Više o tome na https://www.tomislavstankovic.com/blog/rest-api-json-server/ ili https://www.tomislavstankovic.com/blog/jednostavan-nodejs-expressjs-rest-api/
mozes li postaviti gotov projekat na github i podelit link da moze da se skine probao sam i nemogu da se snadjem da mi sve to funkcionise , hvala
Pozdrav Gorane,
nažalost više nemam kopiju projekta. Možeš mi reći do kojeg dijela si stigao i gdje je zapelo pa ti mogu pomoći.
Prema ovom blog postu možeš vidjeti kako se kreira servis i kako se koristi unutar komponente.
Navodim to jer Angular u novim verzijama koristi
import { HttpClient, HttpHeaders } from '@angular/common/http';
umjestoimport { Http } from '@angular/http';
.Pre svega Tomislave hvala ti puno na odgovoru,imam problem ocu da dobijem istu formu imenika kao ti ali nerazumem uputstvo instalirao sam angular kreiram projekat na pocetku ali od dela
Kreiranje servisa za API
Neznam da upisem ove kodove.Recimo kad kreiram —–
korisnici-api.service.ts
tu krenem da upisujem kod to zavrsim i onda kada dodjem do ovog dela
Izgled forme (app.component.html)
To kad otvorim tu vec ima dosta toga upisanog neznam gde da postavim kod koji si ti naveo,prvi put se susrecem sa angular pa mi bas tesko ide ja kad ovo odradim kako mislim sve mi se raspadne.Molim te pomozi hvala ti unapred
Kada kreiraš novi projekt unutar app.component.html se nalazi početni sadržaj čisto iz razloga da ti se nešto prikaže na ekranu kada pokreneš projekt sa “ng serve”. Znači, možeš sve unutar app.component.html obrisati i kopirati ono što sam ja napravio tj. formu.
Ako si me razumeo ,mislim ovo nerazumem gde da postavim tvoj kod od ovog dela pa na dalje
rekao si kako izgleda neznam kako to da sredim ja od tog dela upisem tvoj kod ispo tog teksta sto si stavio znaci editujem app.component.html
ali onda mi se sve raspadne negde gresim neznam gde.
Pretpostavljam da ti se forma raspadne zbog Bootstrapa. Možeš ga importati i unutar “.angular-cli.json”.
Instaliraj Bootstrap koristeći “npm i bootstrap” i onda unutar “.angular-cli.json” napravi sljedeće:
"styles": [
"../node_modules/bootstrap/dist/css/bootstrap.min.css",
...
"./assets/css/main.css"
],
Također, iz HTML forme izbaci sve gdje se spominje
ng-class
,*ngIf
,{{xx}}
,*ngFor
,(click)
,[(ngModel)]
,[ngModelOptions]
,="ngModel"
i[disabled]=
. To ćeš kasnije sve vratiti nakon što se forma ispravno prikaže.Cilj ovoga je da ti se na ekranu prikaže forma. A tek nakon toga možemo prijeći na funkcionalnosti.
Hvala ti puno na pomoci sad mi je jasnije
Izvini,ali sta da radim moram jos nesto da te pitam meni sada ispada ova greska pogledaj bice ti jasno sve hvala ti na pomoci.
https://i.postimg.cc/PrMK2cLz/Greska.jpg
Provjeri imaš li
import {FormsModule} from '@angular/forms';
u app.module.ts. Primjer možeš pronaći u službenoj dokumentaciji https://angular.io/guide/formsNakon što to ubaciš više nećeš imati te greške i forma bi ti se trebala prikazati, ali ćeš imati drugih grešaka koje se tiču app.component.ts datoteke ako unaprijed nisi definirao sve varijable i metode koje koristiš u HTML-u. Ako zapneš javi.
Dodao sam ovo u app.module.ts
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { HeroFormComponent } from './hero-form/hero-form.component';
@NgModule({
imports: [
BrowserModule,
FormsModule
],
declarations: [
AppComponent,
HeroFormComponent
],
providers: [],
bootstrap: [ AppComponent ]
})
export class AppModule { }
I Sada mi javlja ovu gresku
https://i.postimg.cc/MZh3H4Nz/Greska-2.jpg
A kada otvorim stranicu pise mi Cannot GET /
nema get metode.
Višak ti je
HeroFormComponent
, osim ako sam nisi kreirao tu komponentu.Evo odradih sada da samo dodam
{FormsModule} from ‘@angular/forms’;
u app.module.ts tako sam te razumeo
onda mi javlja ovu gresku
https://i.postimg.cc/VNqmZntC/greska-3.jpg
Sta da radim oprosti sto ovoliko pisem al mi je bitno da ovo uspijem da napravim,ucim javascript skolu pocinjem sa svim tim pa mi treba vise rada hvala ti puno jos jednom na pomoci.
To ti se može dogoditi ako si dodao
BrowserModule
podimports
, a nisi na vrhu dokumenta naveo na kojoj ga je putanji moguće pronaći.import { BrowserModule } from '@angular/platform-browser';
...
imports: [
BrowserModule,
...
],
P.S. Ako želiš naučiti JavaScript preporučio bi ti da, prije nego dublje uđeš u Angular, prođeš knjigu “A Smarter Way to Learn JavaScript” i online vježbe na adresi http://www.asmarterwaytolearn.com/js/index-of-exercises.html.
Vazi hvala ti puno pogledacu
Mogu li te pitati nesto postoji li sansa da odradis ovo i postavis projekat da se skine preko githuba puno bi mi znacilo nikako nemogu da se snadjem sta god da odradim javlja mi gresku molim za tu pomoc ako mozes to da uradis,pokusavam sve sto mogu ali mi nikako neide a ti bi to odma odradio.Hvala ti unapred za svu pomoc do sada a i za ovo sto ces nadam se moci da mi uradis.
Možeš kao temelj iskoristiti projekt https://github.com/tomislavstankovic/api-sudskog-registra. Temeljen je na Angular 10, dok je ovaj iz blog posta Angular 4. Imaš u njemu HTML formu, samo si prilagodi polja.
Na GitHubu se nalazi i API https://github.com/tomislavstankovic/SImple-NodeJS-ExpressJS-Starter-API
Hvala ti pokusacu bitno mi je da dobijem samo taj imenik
Evo opet mene samo da te pitam sada mi javlja ovu gresku neznam zasto sve odradim kako si mi rekao gledo sam ponovo sta si mi do sada pisao i sada mi ovo izbaca
https://i.postimg.cc/wxCJx8jg/Greska.png
Odgovorio sam u komentaru ispod. Postoji višak koda u app.module.ts koji uzrokuje tu grešku.
Evo sta sam dodavao pa mi to kada sam dodao ovo javlja
https://i.postimg.cc/MZQMFPb4/greska-2.png
Višak ti je prvih pet linija tj. dva puta imaš import BrowserModule. Nisi trebao taj dio na vrhu kopirati ako ga već imaš u app.module.ts