CRUD – Create, Read, Update, Delete – az adatbázis-akciók négy szent grálja. Ezek az egyetlenek, amikre valaha is szükséged lesz ahhoz, hogy bármi jelentőset csinálj az adatbázisoddal. Persze, növelheted a lekérdezések bonyolultságát, de végső soron minden erre a négy műveletre fut ki.
A Firebase egy Google tulajdonában lévő felhő alapú rendszer, amely API horgokkal, fájltároló hellyel, auth rendszerrel és tárhely funkciókkal rendelkezik. Ez egy sokkal alulértékelt rendszer, amelyet inkább prototípusok készítésére és gyors alkalmazásfejlesztésre kellene használni.
Ha egy progresszív webes alkalmazást szeretnél elindítani, de nincs meg a backend tapasztalatod a szerverek beállításához, API-k létrehozásához és adatbázisokkal való foglalkozáshoz, akkor a Firebase fantasztikus lehetőség a frontend fejlesztők számára, akik elszigeteltnek és elakadtnak érezhetik magukat a hatalmas információhegy miatt, amelyet fel kell dolgozniuk, hogy egyáltalán elindíthassák az alkalmazásukat.
Vagy ha egyszerűen csak kevés az időd, a Firebase szinte a felére csökkentheti a fejlesztési órákat, így a felhasználói élményre és a felhasználói felület áramlásainak megvalósítására koncentrálhatsz. Emellett elég rugalmas ahhoz, hogy szükség esetén kivonja a frontend-alkalmazását, és más adatbázist használjon.
Itt egy gyors útmutató arról, hogyan lehet CRUD műveleteket megvalósítani az Angular és a Firebase segítségével.
Ez egy csupasz kávérendelő alkalmazás, ahol rendeléseket adhatunk hozzá (create), rendeléseket listázhatunk az adatbázisból (read), rendelést jelölhetünk meg befejezettnek (update) és rendelést távolíthatunk el (delete).
A bemutató célja, hogy segítsen a Firebase Firestore-ral való ismerkedésben, és hogy lássuk, milyen egyszerű csatlakozni és használni a Google tulajdonában lévő szolgáltatást. Ez nem a Google reklámja (nem kapok visszaosztást tőlük ezért), hanem pusztán annak illusztrálása, hogyan játszik az Angular az adatbázissal.
Az alkalmazás, amit készíteni fogunk, nem tökéletes. Hiányoznak olyan dolgok, mint az adatérvényesítés és millió lehetséges funkció, amit én is hozzáadhatok. De nem ez a lényeg. A lényeg az, hogy a lehető leggyorsabban beállítsuk Angularban és működésre bírjuk egy élő adatbázissal.
Szóval, elég a bevezetőből – itt a kód végigjárása.
A kezdeti beállítás
Az Angular alkalmazás beállítása az Angular CLI-n keresztül. Ha még nincs Angular CLI-d, itt találsz róla bővebb információt.
Röviden, egyszerűen futtasd ezeket a parancsokat a terminálodban abban a könyvtárban, ahol az Angular alkalmazásodat szeretnéd elhelyezni. Itt vannak a parancsok, és hogy mit csinálnak.
npm install -g @angular/cli
Installálja az Angular CLI-t a terminálodon, ha még nem rendelkezel vele.
ng new name-of-app-here
Ez egy új Angular projektet hoz létre az Angular elérhető legújabb verziójával.
cd name-of-app-here
Amikor az Angular CLI segítségével létrehozol egy új projektet, létrehoz egy új projekt mappát neked. cd
Ezzel a terminálon keresztül eljutunk ebbe a mappába.
ng serve
Ezzel elindítjuk és futtatjuk az Angular projektet.
Az Angular beállítása
Ezzel az Angular alkalmazással összesen 3 új részt fogunk létrehozni: orders, order-list és shared/ordersService. Az első kettő komponens, amely az alkalmazás felületét fogja tartalmazni, a shared/orders service pedig az összes Firebase API hívást fogja egyben tartani.
A szükséges fájlok létrehozásához futtassuk le a következő parancsokat:
ng g c orders
g
a generate és c
a component rövidítése. A parancs utolsó része a fájl neve, amely a fenti esetben az orders nevet viseli. Ugyanezt a hatást elérheted a ng generate component orders
használatával is.
Elkészíthetsz egy másik komponenst a order-listához a következő paranccsal:
ng g c order-list
Végül pedig a szolgáltatáshoz a c
helyett a s
-t használd az alábbiak szerint:
ng g s shared/orders
Ez egy orders.service.ts
fájlt hoz létre a shared nevű mappában. Ügyeljen arra, hogy a orders.service.ts
-et hozzáadja a app.module.ts
-hez, mert ez nem történik meg ön helyett automatikusan, mint a komponenseknél. Ezt a import
segítségével és a providers
listához való hozzáadással teheted meg így:
import { OrdersService } from "./shared/orders.service";
...
providers:
...
A CSS
Ebben a bemutatóban a Materialize CSS-t fogjuk használni, hogy a végleges alkalmazásunk jobban nézzen ki, mint az alapértelmezett css. Ez nem szükséges, és bármilyen CSS keretrendszert vagy saját egyéni CSS-t használhatsz, ha akarod. A Materialize CSS részleteit itt nézheted meg.
A Googles anyagi ikonjait fogjuk használni gombokként is, hogy a kávérendelést befejezettnek jelöljük vagy töröljük a rendelést.
Ez megvalósításának egyik módja, hogy az alábbi kódot közvetlenül a </head>
tag felett helyezzük el a index.html
fájlban, amely a src
mappában található.
<!-- Compiled and minified Materialize CSS -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
<!-- Compiled and minified Materialize JavaScript -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script><!-- Google Material Icons -->
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet" />
A kezdeti Angular nézet kódja
A app.component.html
-ben töröljük az összes indító kódot. Ehhez a saját tartalmunkat fogjuk használni.
A most létrehozott komponensre fogunk rákapcsolódni, és a app-orders
és app-order-list
szelektorok segítségével megjeleníteni őket a képernyőn. Ehhez az alábbi kódot írjuk:
<div class="container">
<div class="row">
<app-orders class="col s6"></app-orders>
<app-order-list class="col s6"></app-order-list>
</div>
</div>
A container
, row
, col
és s6
osztályok a Materialize CSS rácsrendszerének részei. Minden osztály, amit a bemutató további részében látni fogsz, a Materialize CSS-ből származik, hacsak másképp nem említjük.
Az űrlap beállítása
Az űrlapok beállításához importáld a ReactiveFormsModule
-t a app.module.ts
-ben, és ne felejtsd el importálni a imports
tömbben.
import { ReactiveFormsModule } from "@angular/forms";
A orders.service.ts
-ben importáljuk a FormControl
és a FormGroup
-t a @angular/forms
-ból, és hozzunk létre egy új űrlapot a constructor
-on kívül, ahol beállíthatjuk az űrlap tulajdonságait az alábbiak szerint:
import { FormControl, FormGroup } from "@angular/forms";
...
...export class OrdersService {
constructor() {}
form = new FormGroup({
customerName: new FormControl(''),
orderNumber: new FormControl(''),
coffeeOrder: new FormControl(''),
completed: new FormControl(false)
})
}
Ezeket az értékeket a Firebase-ben fogjuk tárolni egy kicsit később ebben a bemutatóban.
A nyomtatvány használata egy komponensen belül
import
a OrdersService
osztályt a komponensedbe, így használhatod az objektumot a komponenseden belül, majd létrehozhatod az osztály egy objektumát a constructor
-ban.
import { OrdersService } from "../shared/orders.service";
...
constructor(private ordersService:OrdersService){}
A orders.component.html
-ban használd a formGroup
direktívát, hogy hivatkozz a OrdersService
-ban létrehozott űrlap objektumra. Minden formControlName
a OrdersService
osztályon belül létrehozott formGroup
-ben használt nevekre hivatkozik. Ez lehetővé teszi a kapcsolódó vezérlő számára, hogy az űrlapba beírt változókat használja. Lásd az alábbi kódot:
<form ="this.ordersService.form">
<input placeholder="Order Number"
formControlName="orderNumber"
type="text"
class="input-field col s12">
<input placeholder="Customer Name"
formControlName="customerName"
type="text"
class="input-field col s12">
</form>
A orders.component.ts
-ben egy kávákból álló tömböt fogunk létrehozni a orders.component.html
-unkon történő loopoláshoz. Elméletileg ezt a Firestore adatbázisodon belül is beállíthatod, meghívhatod a gyűjteményt, majd használhatod. De a hosszúság kedvéért helyi tömbként fogjuk beállítani. A OrdersComponent
osztályodon belül állítsd be a következő tömböt:
coffees = ;
A orders.component.html
és a <form>
címkéken belül a *ngFor
segítségével egy (click)
akciókezelővel hurkold át, hogy új kávékat adj hozzá a rendelésedhez. A rendelési listát közvetlenül alatta fogjuk megjeleníteni az egyes kávék eltávolításának lehetőségével a következők szerint:
<button class="waves-effect waves-light btn col s4"
*ngFor="let coffee of coffees"
(click)="addCoffee(coffee)">
{{coffee}}
</button><ul class="collection">
<li *ngFor="let coffee of coffeeOrder">
<span class="col s11"> {{ coffee }} </span>
<a class="col s1" (click)="removeCoffee(coffee)">x</a>
</li>
</ul>
A orders.component
belsejében létrehozol egy üres tömböt a kávék rendelésének elhelyezésére, a addCoffee()
függvényt használod az új kávék hozzáadására, és a removeCoffee()
függvényt az italok eltávolítására a rendelési listából.
coffeeOrder = ;addCoffee = coffee => this.coffeeOrder.push(coffee);removeCoffee = coffee => {
let index = this.coffeeOrder.indexOf(coffee);
if (index > -1) this.coffeeOrder.splice(index, 1);
};
A nyomtatvány benyújtásának kezelése
Adj hozzá egy Submit Order bemenetet a <form>
címkék belsejében és alján, és add hozzá a onSubmit()
egy kattintáskezelőhöz az alábbiak szerint:
<form ="this.ordersService.form" (ngSubmit)="onSubmit()"> ...
<button
class="waves-effect waves-light btn col s12"
(click)="onSubmit()">
Submit
</button>
</form>
Készíts egy üres onSubmit
függvényt a orders.component
-ben a köztes időre. Hamarosan eseménykezelést fogunk hozzáadni. Ezen a ponton az űrlapodnak készen kell állnia a Firebase adatbázishoz való csatlakozásra.
A listázó komponens beállítása
Most már csak egy helyre van szükségünk, ahol megjeleníthetjük a kávérendeléseinket az adatbázisból. Egyelőre csak az állvány html-jét állítjuk be.
Navigáljunk a order-list.component.html
-ba, és hozzunk létre egy táblázatot 3 fejléccel és 5 adatcellával. Az első 3 adatcella az adatbázisból húzott értékeket fogja tartalmazni, az utolsó 2 pedig extra funkciókat, amelyek lehetővé teszik a megrendelés teljesnek jelölését vagy a megrendelés törlését.
A Firebase beállítása
Lépjen a Firebase konzoljára, és adjon hozzá egy új projektet.
Adjon egy projektnevet, fogadja el a feltételeket és kattintson a projekt létrehozására.
Amikor a Firebase projekt beállítása megtörtént, valami ilyesmit fog látni.
Hozzon létre egy adatbázist az oldalsó panelen (a Develop alatt található) Database kiválasztásával, majd kattintson a Cloud Firestore banner alatt található Create Database-ra.
A biztonsági szabályoknál válassza a start in test mode lehetőséget. Ezt később módosíthatja.
Egy üres Firestore adatbázist fog kapni, mint ez.
A Firebase Firestore csatlakoztatása az Angularhoz
Az Angular alkalmazásod Firebase-hez való csatlakoztatásához telepítened kell a firebase
és @angular/fire
csomagokat. Ezáltal hozzáférhetsz a AngularFireModule
és AngularFirestoreModule
csomagokhoz. Használd a következő parancsot a terminálodban a telepítésükhöz.
npm i --save firebase @angular/fire
A csatlakozók implementálása
Menj vissza a Firebase webkonzolodba, és szerezd be a konfigurációs adatokat, hogy használhasd az Angular alkalmazásodban. Ez a projekt áttekintőoldalán található. Valahogy így néz ki:
Kopírozd ki a kód kiemelt részét, navigálj a environment.ts
fájlodhoz (amely a environments
mappában található), és illeszd be a konfiguráció adatait a environment
objektumon belül firebaseConfig
ként. Lásd alább a példát:
export const environment = {
production: false,
firebaseConfig: {
apiKey: "xxx",
authDomain: "xxxx.firebaseapp.com",
databaseURL: "https://xxxx.firebaseio.com",
projectId: "xxxx",
storageBucket: "xxxx.appspot.com",
messagingSenderId: "xxxxx"
}
};
Importáld a korábban telepített csomagokat és a environment.ts
fájlt a app.module.ts
-ödbe.A AngularFireModule
-t inicializálnod kell a firebaseConfig
-vel, aminek a beállítását most elvégezted. Lásd az alábbi példát:
import { environment } from "src/environments/environment";
import { AngularFireModule } from "@angular/fire";
import { AngularFirestoreModule } from "@angular/fire/firestore";
...
@NgModule({
...
imports:
...
})
A Firestore beállítása egy szolgáltatásban
Importáld a AngularFirestore
fájlt a orders.service.ts
fájlodba és deklaráld a konstruktorban, hogy a szolgáltatásod tudjon róla.
...
import { AngularFirestore } from '@angular/fire/firestore';
...
export class OrdersService {
constructor( private firestore: AngularFirestore ) {}
...
}
Most már minden készen áll, és készen állsz arra, hogy elkezdd a CRUD részét ennek az Angular + Firebase Firestore bemutatónak.
C is for Create
Hogy létrehozz egy új rekordot a vadonatúj Firestore adatbázisodban, meg kell hívnod a .add()
. Ezt a orders.service.ts
fájlon belül fogjuk megtenni.
Ezhez meg kell adnod a collection
nevet és azt, hogy milyen adatokat akarsz az adatbázisba tolni. A mi esetünkben ez coffeeOrders
.
A lenti példakód egy ígéretet használ a Firebase hívás visszaadására, majd hagyja, hogy eldöntse, mit szeretne tenni, miután az egész befejeződött.
...
createCoffeeOrder(data) {
return new Promise<any>((resolve, reject) =>{
this.firestore
.collection("coffeeOrders")
.add(data)
.then(res => {}, err => reject(err));
});
}
...
A funkció meghívásához a komponensedben navigálj a orders.component.ts
-be, és a onSubmit()
függvényen és műveletkezelőn belül hívd meg a createCoffeeOrder()
-t a ordersService
-on keresztül.
A lenti példa némi adatfeldolgozást végez a coffeeOrder
tömb coffeeOrder
űrlapértékre való leképezésével. Létrehoztam egy data
változót is a szerkezetátalakítás céljából (azzal együtt, hogy nem kell egy masszívan hosszú nevet átadni a createCoffeeOrder()
-be).
...
onSubmit() {
this.ordersService.form.value.coffeeOrder = this.coffeeOrder;
let data = this.ordersService.form.value;
this.ordersService.createCoffeeOrder(data)
.then(res => {
/*do something here....
maybe clear the form or give a success message*/
});
}
...
És viola! Most már létrehoztál egy rekordot a localhost Angular alkalmazásodból a Firestore adatbázisodba.
R is for Read
A kávé rendelések adatainak megjelenítéséhez szükséged lesz egy read függvényre a orders.service.ts
-ben. A következő kódpélda a coffeeOrders
gyűjteményedben tárolt összes értéket megadja.
...
getCoffeeOrders() {
return
this.firestore.collection("coffeeOrders").snapshotChanges();
}
...
A függvény használatához meg kell hívnod a orders-list.component.ts
-ból . Ezt úgy teheti meg, hogy importálja a OrdersService
fájlba, és inicializálja a constructor
-ban.
...
import { OrdersService } from "../shared/orders.service";
...export class OrderListComponent implements OnInit {
constructor(private ordersService:OrdersService){}
...
}
Készítsen egy függvényt, amely tartalmazza a hívását, és inicializálja azt a ngOnInit()
-ban, hogy a nézet első betöltésekor hívja meg. Hozzon létre egy coffeeOrders
változót az subscribe()
-en keresztül az adatbázisból visszaadott eredmények leképezéséhez. Ezt fogjuk használni a order-list.component.html
...
ngOnInit() {this.getCoffeeOrders();}
... coffeeOrders; getCoffeeOrders = () =>
this.ordersService
.getCoffeeOrders()
.subscribe(res =>(this.coffeeOrders = res));...
or
der-list.component.html
-ben történő iterálásra és megjelenítésre, a *ngFor
segítségével pedig a coffeeOrders
-et használjuk a visszaadott tömbön való végighaladáshoz. Szükséged van egy kis inceptionre is, és egy másik ciklusra a coffeeOrder
részhez, hogy megkapd a kávék listáját minden egyes ügyfélhez. Vannak ennél hatékonyabb módszerek is, de ehhez a bemutatóhoz lásd az alábbi példakódot:
...
<tbody>
<tr *ngFor="let order of coffeeOrders">
<td>{{ order.payload.doc.data().orderNumber }}</td>
<td>{{ order.payload.doc.data().customerName }}</td>
<td><span
*ngFor="let coffee of order.payload.doc.data().coffeeOrder">
{{ coffee }}
</span>
</td>
</tr>
</tbody>
...
És meg is van. Most már csatlakoztattad az olvasási képességeket az Angular alkalmazásodhoz.
U is for Update
Tegyük fel, hogy szeretnénk a kávérendelést teljesnek jelölni az adatbázisban, és a változás alapján tenni valamit a sorszámmal. Mivel a snapshot()
nyomon követi a bekövetkező változásokat, nem kell semmilyen nyomon követést vagy lekérdezést végeznünk az adatbázisban.
Elkészítünk egy másik adatcellát a táblázatunkban egy “check” ikonnal a Google Materialize Iconsból, és összekötjük egy (click)
-eseménnyel, amely meghívja a markCompleted()
függvényt a order-list.component.ts
-ban. Átadjuk az adott sorrendet is ehhez a ciklushoz.
Egy attribútumot fogunk beállítani az
completed
értékkel az adatcellához, hogy az dinamikusan meghatározhassa, hogy szeretnénk-e a ‘check’ ikont megjeleníteni. Ezt az értéket eredetileg false
-ként állítottuk be, amikor először létrehoztuk az űrlapot a orders.service.ts
.
...
<td ="order.payload.doc.data().completed"
(click)="markCompleted(order)">
<i class="material-icons">check</i>
</td>
...
Inside order-list.component.ts
-ban , hozzuk létre a markCompleted()
függvényt, amely a beadott data
-t használja, hogy átadja a updateCoffeeOrder()
nevű függvénynek a orders.service.ts
-ben.
...
markCompleted = data =>
this.ordersService.updateCoffeeOrder(data);
...
Inside orders.service.ts
hozzuk létre a kezelőfüggvényt. Ez a függvény csatlakozik és hívja a Firestore adatbázisát a kiválasztott gyűjtemény és dokumentum azonosító alapján. Már tudjuk, hogy a gyűjteményünk neve coffeeOrders
, és a dokumentum azonosítóját a komponens függvényhívásból átadott paraméterek alapján találjuk meg.
.set()
beállítja az adott rekordot bármilyen átadott adattal. A .set()
két paramétert vesz át – az adatainkat és egy beállítási objektumot. Ha merge: true
-t használsz, akkor ez azt jelenti, hogy csak az átadott érték-kulcs párt frissíted, nem pedig az egész dokumentumot cseréled le arra, amit átadtál.
...
updateCoffeeOrder(data) {
return
this.firestore
.collection("coffeeOrders")
.doc(data.payload.doc.id)
.set({ completed: true }, { merge: true });
}
...
Most, amikor a nézetedben a ‘check’ ikonra kattintasz, az frissíti az adatbázisodat, és eltűnik, mert a attribútumod most
true
-ra van állítva.
D is for Delete
Az utolsó művelethez mindent a frissítési folyamathoz hasonlóan fogunk beállítani – de a rekord frissítése helyett törölni fogjuk azt.
Egy másik adatcellát állítunk be egy kattintási eseménykezelővel, amely meghív egy deleteOrder()
függvényt. A order
adatpéldányt fogjuk átadni a ciklusban, amikor a ‘delete_forever’ ikonra kattintunk. Erre a dokumentum azonosítójához lesz szükségünk. Lásd az alábbi példakódot:
...
<td ="order.payload.doc.data().completed"
(click)="deleteOrder(order)">
<i class="material-icons">delete_forever</i>
</td>
...
A order-list.component.ts
fájlodban hozd létre a kapcsolódó függvényt, amely meghív egy deleteCoffeeOrder()
függvényt a orders.service.ts
fájlodban .
...
deleteOrder = data => this.ordersService.deleteCoffeeOrder(data);
...
A orders.service.ts
fájlodban hozd létre a deleteCoffeeOrder()
függvényt, és használd a data
injektálást, hogy kitaláld, mi a dokumentum azonosítója. A frissítéshez hasonlóan a gyűjtemény nevét és a dokumentum azonosítóját is ismernie kell ahhoz, hogy helyesen azonosítani tudja, melyik rekordot szeretné törölni. A .delete()
segítségével közölje a Firestore-ral, hogy törölni szeretné a rekordot.
...
deleteCoffeeOrder(data) {
return
this.firestore
.collection("coffeeOrders")
.doc(data.payload.doc.id)
.delete();
}
...
Most, amikor a ‘delete_forever’ ikonra kattint, az Angular-alkalmazása elindul, és közli a Firestore-ral, hogy törölje az adott rekordot. A rekord eltűnik a nézetből, amikor a megadott dokumentum törlésre kerül.
Végleges alkalmazás
A teljes munkaprojekt Github tárolóját itt találod. Saját Firebase adatbázist kell létrehoznod, és a konfigurációs fájlok frissítésével magadnak kell bekötnöd. Maga a kód nem tökéletes, de nagyon minimálisra csökkentettem, így láthatod, hogyan működik a Firestore egy Angular alkalmazásban anélkül, hogy egy dzsungelnyi kódot kellene átnézned.
A bemutatót igyekeztem a lehető legjobban összesűríteni anélkül, hogy kihagynék bármilyen részletet. Nehéz volt a keverés, de ahogy fentebb láthatod, nagy része főleg az Angular kód és nem sok Firestore. A Firebase Firestore sokkal összetettebb lekérdezésekre képes, de a szemléltetés érdekében egyszerűnek tartottam. Elméletileg, ha az adatszerkezeted ugyanaz marad, akkor minimális refaktorálással kicserélheted a Firestore-t és más API-kapcsolatokat helyezhetsz el.
Végszavak
Személy szerint azért szeretem a Firebase-t, mert könnyen és gyorsan létrehozhatóak a termelésre kész projektek anélkül, hogy egy teljes backendet és szervert kellene létrehozni a támogatáshoz. Költség szempontjából nem túl rossz, és a tesztprojektek indítása ingyenes (és nem kényszerítenek a hitelkártyaadatok megadására, amíg nem állsz készen a csomagváltásra). Összességében több időt vett igénybe az űrlap és az alkalmazást körülvevő felület létrehozása, mint az alkalmazás Firestore-hoz való tényleges csatlakoztatása. A hosszúság miatt nem adtam hozzá űrlap-érvényesítést ehhez a bemutatóhoz, de a jövőben készítek egy másik bejegyzést erről.