CRUD – Create, Read, Update, Delete – de vier heilige graal van database acties. Het zijn de enige dingen die je ooit nodig zult hebben om iets belangrijks met je database te doen. Zeker, je kunt de complexiteit van de queries verhogen, maar aan het eind van de dag, komt het allemaal neer op deze vier acties.
Firebase is een cloud-gebaseerd systeem dat eigendom is van Google en compleet is met API hooks, bestandsopslagruimte, auth-systeem en hosting mogelijkheden. Het is een veel onderschat systeem dat meer zou moeten worden gebruikt voor prototyping en snelle ontwikkeling van toepassingen.
Als u een progressieve webapp wilt opstarten, maar geen backend-ervaring hebt met het opzetten van servers, het maken van API’s en het omgaan met databases, dan is Firebase een fantastische optie voor front-end ontwikkelaars die zich geïsoleerd en vastgelopen kunnen voelen door de enorme heuvel van informatie die ze moeten verwerken om zelfs hun app aan de praat te krijgen.
Of als je gewoon weinig tijd hebt, kan Firebase uw ontwikkelingstijd bijna halveren, zodat u zich kunt concentreren op gebruikerservaring en het implementeren van die UI-stromen. Het is ook flexibel genoeg om uw front-end applicatie te migreren en een andere database te gebruiken indien nodig.
Hier is een snelle gids over hoe CRUD acties te implementeren met Angular en Firebase.
Het is een kale koffiebestel-app waar je bestellingen kunt toevoegen (create), bestellingen uit de database kunt oplijsten (read), bestellingen als voltooid kunt markeren (update) en een bestelling kunt verwijderen (delete).
Het doel van deze tutorial is om je te helpen aan de slag te gaan met Firebase Firestore en te zien hoe eenvoudig het is om verbinding te maken met en aan de slag te gaan met de service die eigendom is van Google. Dit is geen reclame voor Google (ik krijg geen kickbacks van hen voor dit), maar slechts een illustratie van hoe Angular speelt met de database.
De app die we gaan maken is niet perfect. Er ontbreken dingen zoals data validatie en een miljoen mogelijke functies die ik ook kan toevoegen. Maar daar gaat het niet om. Het punt is om zo snel mogelijk in Angular op te zetten en het werkend te krijgen met een live database.
Zo, genoeg van de intro – hier is de code walk through.
De initiële setup
Stel je Angular app op via de Angular CLI. Als je nog geen Angular CLI hebt, kun je er hier meer over vinden.
In het kort, voer gewoon deze commando’s uit in je terminal in de directory waar je je Angular app wilt hebben staan. Dit zijn de commando’s en wat ze doen.
npm install -g @angular/cli
Installeert Angular CLI op uw terminal als u deze nog niet heeft.
ng new name-of-app-here
Hiermee maakt u een nieuw Angular-project aan met de nieuwste versie van Angular die beschikbaar is.
cd name-of-app-here
Wanneer u een nieuw project aanmaakt via Angular CLI, dan wordt er een nieuwe projectmap voor u aangemaakt. cd
brengt u via de terminal naar die map.
ng serve
Dit is het starten en uitvoeren van uw Angular-project.
Opzetten van Angular
We gaan in totaal 3 nieuwe onderdelen maken voor deze Angular-app – orders, order-list en shared/ordersService. De eerste twee zijn componenten die de interface voor de app zullen bevatten en de shared/orders service die alle Firebase API calls bij elkaar zal houden.
Om de benodigde bestanden aan te maken, voert u de volgende commando’s uit:
ng g c orders
g
staat voor generate en c
staat voor component. Het laatste deel van het commando is de naam van uw bestand, dat in het bovenstaande geval orders heet. U kunt ook ng generate component orders
gebruiken om hetzelfde effect te bereiken.
Maak nog een component voor order-lijst met het volgende commando.
ng g c order-list
En tenslotte, voor de service, gebruik s
in plaats van c
zoals hieronder:
ng g s shared/orders
Dit zal een orders.service.ts
bestand maken in een map met de naam shared. Zorg ervoor dat u orders.service.ts
toevoegt aan app.module.ts
omdat dit niet automatisch voor u wordt gedaan zoals de componenten. U kunt dit doen via import
en het toevoegen aan de providers
lijst zoals dit:
import { OrdersService } from "./shared/orders.service";
...
providers:
...
De CSS
Voor deze tutorial, zullen we Materialize CSS gebruiken om onze uiteindelijke applicatie er beter uit te laten zien dan de standaard css. Het is niet noodzakelijk en u kunt elk CSS framework of uw eigen aangepaste CSS gebruiken als u dat wilt. U kunt de details van Materialize CSS hier.
We gaan ook Googles materiaal pictogrammen te gebruiken als knoppen om de koffie bestelling te markeren als voltooid of verwijder de bestelling.
Een manier om dit te implementeren is om de onderstaande code direct boven de </head>
tag in uw index.html
bestand in de src
map te plaatsen.
<!-- 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" />
De initiële Angular view code
In app.component.html
, verwijder alle starter code. We zullen onze eigen inhoud hiervoor gebruiken.
We zullen inhaken op de component die we zojuist hebben gemaakt en deze op het scherm weergeven met behulp van de selectors app-orders
en app-order-list
. Om dit te doen, schrijven we de onderstaande code:
<div class="container">
<div class="row">
<app-orders class="col s6"></app-orders>
<app-order-list class="col s6"></app-order-list>
</div>
</div>
De klassen container
, row
, col
en s6
maken deel uit van het Materialize CSS-rastersysteem. Alle klassen die u zult zien in de rest van deze tutorial zijn van Materialize CSS, tenzij anders vermeld.
Opzetten van het formulier
Om formulieren op te zetten, importeer ReactiveFormsModule
in app.module.ts
en vergeet niet om het te importeren in de imports
array.
import { ReactiveFormsModule } from "@angular/forms";
In orders.service.ts
importeer FormControl
en FormGroup
uit @angular/forms
en maak een nieuw formulier buiten de constructor
waar u de eigenschappen van het formulier kunt instellen zoals hieronder:
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)
})
}
We zullen deze waarden gebruiken om op te slaan in Firebase een beetje later in deze tutorial.
Het formulier binnen een component gebruiken
import
de OrdersService
klasse in uw component zodat u het object binnen uw component kunt gebruiken en vervolgens een object van de klasse binnen de constructor
kunt maken.
import { OrdersService } from "../shared/orders.service";
...
constructor(private ordersService:OrdersService){}
Binnen uw orders.component.html
gebruikt u de formGroup
richtlijn om naar het in OrdersService
gemaakte formulierobject te verwijzen. Elke formControlName
verwijst naar de namen die we in de formGroup
in de OrdersService
class hebben gebruikt. Hierdoor kan de bijbehorende controller de variabelen gebruiken die in het formulier zijn getypt. Zie de code hieronder:
<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>
In de orders.component.ts
, gaan we een array van koffie opzetten om door te lussen op onze orders.component.html
. In theorie zou je dit ook in je Firestore database kunnen opzetten, een oproep doen naar de collectie en het dan gebruiken. Maar omwille van de lengte, gaan we het opzetten als een lokale array. In je OrdersComponent
klasse, stel je de volgende array in:
coffees = ;
In je orders.component.html
en in je <form>
tags, loop er doorheen met *ngFor
met een (click)
action handler om nieuwe koffies aan je bestelling toe te voegen. We gaan de bestellijst hieronder weergeven met de mogelijkheid om individuele koffie te verwijderen als volgt:
<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>
Binnen orders.component
maak je een lege array om de koffiebestelling te huisvesten, gebruik je de functie addCoffee()
om nieuwe koffie toe te voegen, en removeCoffee()
om een drank van je bestellijst te verwijderen.
coffeeOrder = ;addCoffee = coffee => this.coffeeOrder.push(coffee);removeCoffee = coffee => {
let index = this.coffeeOrder.indexOf(coffee);
if (index > -1) this.coffeeOrder.splice(index, 1);
};
Handling formulier indiening
Voeg een Submit Order input toe in en onderaan de <form>
tags en voeg de onSubmit()
toe aan een click handler zoals hieronder:
<form ="this.ordersService.form" (ngSubmit)="onSubmit()"> ...
<button
class="waves-effect waves-light btn col s12"
(click)="onSubmit()">
Submit
</button>
</form>
Creëer een lege onSubmit
functie in uw orders.component
voor de tussentijd. We zullen hier binnenkort event handling aan toevoegen. Op dit punt, zou uw formulier klaar moeten zijn om aan uw Firebase database te koppelen.
Opzetten van de listing component
Nu hebben we alleen nog een ruimte nodig om onze koffiebestellingen uit de database weer te geven. Voor nu, gaan we alleen de steiger html opzetten.
Navigeer naar je order-list.component.html
en maak een tabel met 3 koppen en 5 datacellen. De eerste 3 cellen bevatten de waarden uit de database en de laatste 2 cellen bevatten extra functionaliteit waarmee u de bestelling als compleet kunt markeren of verwijderen.
Instellen Firebase
Ga naar uw Firebase-console en voeg een nieuw project toe.
Voeg een projectnaam toe, accepteer de algemene voorwaarden en klik op project aanmaken.
Wanneer uw Firebase-project is opgezet, ziet u iets als dit.
Maak een database door Database te selecteren in het zijpaneel (onder Ontwikkelen) en klik vervolgens op Database maken onder de Cloud Firestore-banner.
Selecteer Start in testmodus voor beveiligingsregels. U kunt deze later wijzigen.
U krijgt nu een lege Firestore-database zoals hieronder.
Verbinden van Firebase Firestore met Angular
Om uw Angular app te verbinden met Firebase, dient u firebase
en @angular/fire
pakketten te installeren. Dit zal u toegang geven tot AngularFireModule
en AngularFirestoreModule
. Gebruik het volgende commando in uw terminal om ze te installeren.
npm i --save firebase @angular/fire
Implementeren van de connectoren
Ga terug naar uw Firebase web console en pak de config details om te gebruiken in uw Angular app. Deze bevinden zich op uw Project Overview pagina. Het ziet er ongeveer zo uit:
Kopieer het gemarkeerde gedeelte van de code, navigeer naar uw environment.ts
bestand (dat zich in de environments
map bevindt) en plak de config details in het environment
object als firebaseConfig
. Zie hieronder voor een voorbeeld:
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"
}
};
Importeer de pakketten die u eerder hebt geïnstalleerd en het environment.ts
bestand in uw app.module.ts
.U moet de AngularFireModule
initialiseren met de firebaseConfig
waarvoor u zojuist de set-up hebt gedaan. Zie het voorbeeld hieronder:
import { environment } from "src/environments/environment";
import { AngularFireModule } from "@angular/fire";
import { AngularFirestoreModule } from "@angular/fire/firestore";
...
@NgModule({
...
imports:
...
})
Instellen van Firestore in een service
Importeer AngularFirestore
in uw orders.service.ts
bestand en declareer het in de constructor zodat uw service er van weet.
...
import { AngularFirestore } from '@angular/fire/firestore';
...
export class OrdersService {
constructor( private firestore: AngularFirestore ) {}
...
}
Nu bent u helemaal klaar om aan de slag te gaan met het CRUD gedeelte van deze Angular + Firebase Firestore tutorial.
C is for Create
Om een nieuw record aan te maken in uw splinternieuwe Firestore database, moet u .add()
aanroepen. We gaan dit doen in het orders.service.ts
bestand.
Om dit te doen, moet u de collection
naam opgeven en welke gegevens u naar de database wilt pushen. In ons geval is dat coffeeOrders
.
De onderstaande voorbeeldcode gebruikt een belofte om de Firebase-aanroep terug te sturen en laat u dan beslissen wat u wilt doen nadat het allemaal is gedaan.
...
createCoffeeOrder(data) {
return new Promise<any>((resolve, reject) =>{
this.firestore
.collection("coffeeOrders")
.add(data)
.then(res => {}, err => reject(err));
});
}
...
Om deze functie in uw component aan te roepen, navigeert u naar orders.component.ts
en binnen de onSubmit()
functie en actie handler, doe een oproep de createCoffeeOrder()
via ordersService
.
Het onderstaande voorbeeld doet wat verwerking van de gegevens door de coffeeOrder
array naar de formulierwaarde coffeeOrder
te mappen. Ik heb ook een variabele data
aangemaakt voor destructureringsdoeleinden (samen met het niet hoeven doorgeven van een enorm lange naam in createCoffeeOrder()
).
...
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*/
});
}
...
En viola! U hebt nu een record gemaakt van uw localhost Angular app naar uw Firestore database.
R is voor Read
Om uw koffiebestellingen gegevens weer te geven, ga je een lees-functie nodig in uw orders.service.ts
. Het volgende code-voorbeeld zal u alle waarden geven die zijn opgeslagen in uw coffeeOrders
collectie.
...
getCoffeeOrders() {
return
this.firestore.collection("coffeeOrders").snapshotChanges();
}
...
Om deze functie te gebruiken, moet u hem aanroepen vanuit uw orders-list.component.ts
. U kunt dit doen door de OrdersService
in het bestand te importeren en het te initialiseren in de constructor
.
...
import { OrdersService } from "../shared/orders.service";
...export class OrderListComponent implements OnInit {
constructor(private ordersService:OrdersService){}
...
}
Maak een functie om uw oproep te bevatten en initialiseer deze op de ngOnInit()
om het aan te roepen wanneer de view voor de eerste keer wordt geladen. Maak een coffeeOrders
variabele om de geretourneerde resultaten van uw database via subscribe()
in kaart te brengen. We zullen dit gebruiken om te itereren en weer te geven in order-list.component.html
...
ngOnInit() {this.getCoffeeOrders();}
... coffeeOrders; getCoffeeOrders = () =>
this.ordersService
.getCoffeeOrders()
.subscribe(res =>(this.coffeeOrders = res));...
Om coffeeOrders
in uw or
der-list.component.html
te gebruiken, gebruikt u *ngFor
om door de geretourneerde array te lopen. U moet ook een beetje van de inceptie doen en een andere lus doen voor het coffeeOrder
gedeelte om uw lijst met koffies voor elke klant te krijgen. Er zijn efficiëntere manieren om dit te doen, maar voor deze tutorial, zie de voorbeeldcode hieronder:
...
<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>
...
En daar heb je het. U hebt nu leesmogelijkheden aan uw Angular-applicatie gekoppeld.
U staat voor Update
Laten we zeggen dat we de koffiebestelling in de database als compleet willen kunnen markeren en op basis van de wijziging iets met het regelitem willen doen. Omdat snapshot()
alle veranderingen bijhoudt, hoeft u geen tracking of polling naar de database te doen.
We gaan een andere gegevenscel in onze tabel maken met een ‘check’ pictogram van Google Materialize Icons en deze koppelen aan een (click)
event dat de functie markCompleted()
in uw order-list.component.ts
zal aanroepen. We gaan ook de specifieke volgorde voor deze lus doorgeven.
We gaan een attribuut met de
completed
waarde op de gegevenscel instellen, zodat deze dynamisch kan bepalen of we het ‘controle’-pictogram op de display willen hebben. We hebben deze waarde oorspronkelijk ingesteld als false
toen we het formulier voor het eerst in orders.service.ts
.
...
<td ="order.payload.doc.data().completed"
(click)="markCompleted(order)">
<i class="material-icons">check</i>
</td>
...
Inside order-list.component.ts
creëerden, de functie markCompleted()
die de geïnjecteerde data
gebruikt om door te geven aan een functie genaamd updateCoffeeOrder()
in orders.service.ts
.
...
markCompleted = data =>
this.ordersService.updateCoffeeOrder(data);
...
Inside orders.service.ts
creëer de afhandelingsfunctie. Deze functie zal verbinding maken en uw Firestore database oproepen op basis van de geselecteerde collectie en document id. We weten al dat onze collectie coffeeOrders
heet en we kunnen de document-id vinden op basis van de parameters die zijn doorgegeven in de component functie-aanroep.
.set()
zal het specifieke record instellen met de gegevens die u hebt doorgegeven. .set()
neemt twee parameters op – uw gegevens en een instellingsobject. Als u merge: true
gebruikt, betekent dit dat u alleen het waarde-sleutelpaar bijwerkt dat u hebt doorgegeven, in plaats van het hele document te vervangen door wat u hebt doorgegeven.
...
updateCoffeeOrder(data) {
return
this.firestore
.collection("coffeeOrders")
.doc(data.payload.doc.id)
.set({ completed: true }, { merge: true });
}
...
Als u nu op het pictogram ‘controle’ in uw view klikt, wordt uw database bijgewerkt en verdwijnt deze omdat uw -attribuut nu is ingesteld op
true
.
D staat voor Delete
Voor de laatste actie gaan we alles op dezelfde manier opzetten als het update proces – maar in plaats van het record bij te werken, gaan we het verwijderen.
Stel een andere data cel op met een click event handler die een deleteOrder()
functie aanroept. We gaan de instantie van de order
-gegevens in de lus doorgeven wanneer op het pictogram ‘delete_forever’ wordt geklikt. Je hebt dit nodig voor het document id. Zie de code hieronder voor een voorbeeld:
...
<td ="order.payload.doc.data().completed"
(click)="deleteOrder(order)">
<i class="material-icons">delete_forever</i>
</td>
...
Binnen uw order-list.component.ts
maak de bijbehorende functie die een deleteCoffeeOrder()
functie binnen uw orders.service.ts
aanroept.
...
deleteOrder = data => this.ordersService.deleteCoffeeOrder(data);
...
Binnen uw orders.service.ts
bestand, maak de deleteCoffeeOrder()
functie en gebruik de data
geïnjecteerd om uit te vinden wat het document id is. Net als bij de update, moet u zowel de collectienaam als het document-id weten om correct te identificeren welk record u wilt verwijderen. Gebruik .delete()
om Firestore te vertellen dat je het record wilt verwijderen.
...
deleteCoffeeOrder(data) {
return
this.firestore
.collection("coffeeOrders")
.doc(data.payload.doc.id)
.delete();
}
...
Nu wanneer je op het ‘delete_forever’ icoontje klikt, zal je Angular app afgaan en Firestore vertellen dat het specifieke record verwijderd moet worden. Uw record verdwijnt uit de view wanneer het gespecificeerde document wordt verwijderd.
Eindtoepassing
U kunt de Github repository voor het hele werkende project hier vinden. Je zult je eigen Firebase database moeten maken en deze zelf moeten aansluiten door de config bestanden aan te passen. De code zelf is niet perfect, maar ik heb het heel minimaal gehouden, zodat je kunt zien hoe Firestore werkt in een Angular-app zonder door een oerwoud van code te hoeven gaan.
Ik heb geprobeerd de tutorial zo veel mogelijk in te korten zonder details te missen. Het was een moeilijke mix, maar zoals je hierboven kunt zien, is een groot deel ervan vooral de Angular code en niet veel Firestore. Firebase Firestore kan veel complexere queries doen maar voor demonstratie doeleinden heb ik het simpel gehouden. In theorie, als uw datastructuur hetzelfde blijft, kunt u Firestore verwisselen en een andere set van API-verbindingen met minimale refactoring nodig.
Final Words
Ik persoonlijk hou van Firebase vanwege hoe gemakkelijk het is om snel productie-klare projecten te maken zonder de noodzaak om een hele backend en server te maken om het te ondersteunen. Kostentechnisch valt het mee en testprojecten zijn gratis om op te starten (en je hoeft je creditcardgegevens pas in te voeren als je klaar bent om van plan te veranderen). In het algemeen kostte het meer tijd om het formulier en de interface rond de app te maken dan om de applicatie aan te sluiten op Firestore. Ik heb geen formulier validatie toegevoegd aan deze tutorial vanwege de lengte, maar zal een andere post over te maken in de toekomst.