How to CRUD in Angular + Firebase Firestore

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.

Klik op ‘Project toevoegen’ om een nieuw project aan te maken.

Voeg een projectnaam toe, accepteer de algemene voorwaarden en klik op project aanmaken.

Geef uw project een naam en accepteer de voorwaarden van de controller-controller om een project te maken.

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.

Klik op ‘Database maken’

Selecteer Start in testmodus voor beveiligingsregels. U kunt deze later wijzigen.

Bedenk dat u uw inloggegevens alleen voor veiligheidsdoeleinden op uw lokale machine moet bewaren.

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:

Je moet je eigen credentials gebruiken. De bovenstaande configuratie zal niet werken voor u.

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 order-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.

Plaats een reactie