How to CRUD in Angular + Firebase Firestore

CRUD – Luo, lue, päivitä, poista – tietokantatoimintojen neljä pyhää Graalin maljaa. Ne ovat ainoat asiat, joita tulet koskaan tarvitsemaan tehdessäsi mitään merkittävää tietokannallasi. Toki voit lisätä kyselyiden monimutkaisuutta, mutta loppujen lopuksi kaikki tiivistyy näihin neljään toimintoon.

Firebase on Googlen omistama pilvipohjainen järjestelmä, joka sisältää API-koukut, tiedostojen tallennustilan, auth-järjestelmän ja hosting-ominaisuudet. Se on paljon aliarvostettu järjestelmä, jota pitäisi hyödyntää enemmän prototyyppien luomiseen ja nopeaan sovelluskehitykseen.

Jos haluat käynnistää progressiivisen web-sovelluksen, mutta sinulla ei ole backend-kokemusta palvelimien perustamisesta, API-rajapintojen luomisesta ja tietokantojen kanssa tekemisissä olemisesta, Firebase on loistava vaihtoehto etupään kehittäjille, jotka saattavat tuntea itsensä eristäytyneiksi ja jumiutuneiksi valtavaan tietomäkeen, joka heidän on käsiteltävä saadakseen sovelluksensa edes käyntiin.

Tai jos sinulla on vain vähän aikaa, Firebase voi lyhentää kehitystyöaikasi lähes puoleen, jotta voit keskittyä käyttäjäkokemukseen ja näiden UI-virtojen toteuttamiseen. Se on myös tarpeeksi joustava, jotta voit tarvittaessa siirtää front end -sovelluksesi pois ja käyttää eri tietokantaa.

Tässä on pikaopas CRUD-toimintojen toteuttamiseen Angularin ja Firebasen avulla.

Tässä on pelkistetty kahvitilaussovellus, jossa voit lisätä tilauksia (create), listata tilauksia tietokannasta (read), merkitä tilauksen suoritetuksi (update) ja poistaa tilauksen (delete).

Tämän tutoriaalin tarkoituksena on auttaa sinua pääsemään alkuun Firebase Firestoren kanssa ja näkemään, kuinka helppoa Googlen omistamaan palveluun on muodostaa yhteys ja päästä alkuun. Tämä ei ole mainos Googlelle (en saa heiltä mitään palkkiota tästä), vaan ainoastaan havainnollistaa, miten Angular pelaa tietokannan kanssa.

Sovellus, jonka aiomme tehdä, ei ole täydellinen. Siitä puuttuu asioita kuten tietojen validointi ja miljoona mahdollista ominaisuutta, joita voin myös lisätä. Mutta siitä ei ole kyse. Pointti on perustaa Angularissa mahdollisimman nopeasti ja saada se toimimaan live-tietokannan kanssa.

Nyt riittää esittely – tässä on koodin läpikäynti.

Alkuasetukset

Asetetaan Angular-sovellus Angular CLI:n kautta. Jos sinulla ei vielä ole Angular CLI:tä, voit lukea siitä lisää täältä.

Lyhyesti sanottuna, suorita yksinkertaisesti nämä komennot terminaalissasi hakemistossa, jossa haluat Angular-sovelluksesi olevan. Tässä ovat komennot ja mitä ne tekevät.

npm install -g @angular/cli

Asenntaa Angular CLI:n päätelaitteeseesi, jos sinulla ei vielä ole sitä.

ng new name-of-app-here

Luo uuden Angular-projektin käyttäen uusinta saatavilla olevaa Angular-versiota.

cd name-of-app-here

Kun luot uuden projektin Angular CLI:n avulla, se luo sinulle uuden projektikansion. cd vie sinut terminaalin kautta tuohon kansioon.

ng serve

Tämä on käynnistää ja ajaa Angular-projektisi.

Angularin asettaminen

Luomme tähän Angular-sovellukseen yhteensä 3 uutta osaa – tilaukset, tilausluettelo ja shared/ordersService. Kaksi ensimmäistä ovat komponentteja, jotka sisältävät sovelluksen käyttöliittymän ja shared/orders service, joka pitää kaikki Firebase API-kutsut yhdessä.

Luoaksesi tarvittavat tiedostot, suorita seuraavat komennot:

ng g c orders

g tarkoittaa generate ja c tarkoittaa component. Komennon viimeinen osa on tiedostosi nimi, joka yllä olevassa tapauksessa on nimeltään orders. Voit myös käyttää komentoa ng generate component orders saavuttaaksesi saman vaikutuksen.

Luo toinen komponentti tilauslistalle seuraavalla komennolla.

ng g c order-list

Ja lopuksi käytä palvelussa s komentoa c:n sijasta c kuten alla:

ng g s shared/orders

Tämä luo tiedoston orders.service.ts kansioon nimeltä shared. Muista lisätä orders.service.ts tiedostoon app.module.ts, koska tätä ei tehdä puolestasi automaattisesti kuten komponentit. Voit tehdä tämän import:n kautta ja lisäämällä sen providers-luetteloon näin:

import { OrdersService } from "./shared/orders.service";
...
providers:
...

CSS

Tässä opetusohjelmassa käytämme Materialize CSS:ää saadaksemme lopullisen sovelluksemme näyttämään paremmalta kuin oletus-css. Se ei ole välttämätöntä, ja voit käyttää mitä tahansa CSS-kehystä tai omaa mukautettua CSS:ääsi, jos haluat. Voit tutustua Materialize CSS:n yksityiskohtiin täällä.

Käytämme myös Googlen materiaalikuvakkeita painikkeina, joilla voimme merkitä kahvitilauksen valmiiksi tai poistaa tilauksen.

Yksi tapa toteuttaa tämä on, että alla oleva koodi on aivan </head>-tagin yläpuolella index.html-tiedostossasi, joka sijaitsee src-kansiossa.

<!-- 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" />

Alustava Angular-näkymän koodi

Kohdasta app.component.html poista kaikki aloituskoodi. Käytämme tässä omaa sisältöämme.

Koukutamme juuri luomamme komponentin ja näytämme ne ruudulla käyttämällä valitsimia app-orders ja app-order-list. Tätä varten kirjoitamme alla olevan koodin:

<div class="container">
<div class="row">
<app-orders class="col s6"></app-orders>
<app-order-list class="col s6"></app-order-list>
</div>
</div>

Luokat container, row, col ja s6 ovat osa Materialize CSS-ristikkojärjestelmää. Kaikki luokat, jotka tulet näkemään tämän opetusohjelman loppuosassa, ovat Materialize CSS:stä, ellei toisin mainita.

Lomakkeen asettaminen

Lomakkeiden asettamista varten tuo ReactiveFormsModule app.module.ts:ssä ja muista tuoda se imports-joukkoon.

import { ReactiveFormsModule } from "@angular/forms";

Tuo orders.service.ts:n sisälle @angular/forms:sta FormControl ja FormGroup ja luo uusi lomake constructor:n ulkopuolelle, jossa voit asettaa lomakkeen ominaisuudet seuraavasti:

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)
})
}

Käytämme näitä arvoja Firebaseen tallentamiseen hiukan myöhemmässä vaiheessa tätä opetusohjelmaa.

Lomakkeen käyttäminen komponentin sisällä

import OrdersService-luokan sisällyttäminen komponenttiisi, jotta voit käyttää objektia komponenttisi sisällä ja luoda sitten luokan objektin constructor sisälle.

import { OrdersService } from "../shared/orders.service";
...
constructor(private ordersService:OrdersService){}

Komponenttisi orders.component.html sisällä käytä formGroup-direktiiviä viitataksesi lomakkeen objektiin, joka on luotu OrdersService. Jokainen formControlName viittaa OrdersService-luokan sisälle luodussa formGroup:ssä käyttämiimme nimiin. Näin siihen liittyvä ohjain voi käyttää lomakkeeseen kirjoitettuja muuttujia. Katso koodi alla:

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

orders.component.ts:ssä perustamme array off kahvia varten looping through meidän orders.component.html:ssä. Teoriassa voisit myös asettaa tämän Firestore-tietokantaasi, tehdä kutsun kokoelmaan ja käyttää sitä sitten. Mutta pituuden vuoksi asetamme sen paikallisena joukkona. Määritä OrdersComponent-luokkasi sisällä seuraava array:

coffees = ;

Seuraava joukko

coffees = ;

Käy läpi orders.component.html– ja <form>-tunnisteiden sisällä käyttäen *ngFor-toimintakäsittelijää (click) lisätäksesi uusia kahveja tilaukseesi. Näytämme tilausluettelon aivan alapuolella mahdollisuudella poistaa yksittäinen kahvi seuraavasti:

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

Luot orders.component:n sisälle tyhjän array:n, johon sijoitat kahvitilauksen, käytät toimintoa addCoffee() lisätäksesi uusia kahveja ja removeCoffee() poistaaksesi juoman tilausluettelosta.

coffeeOrder = ;addCoffee = coffee => this.coffeeOrder.push(coffee);removeCoffee = coffee => {
let index = this.coffeeOrder.indexOf(coffee);
if (index > -1) this.coffeeOrder.splice(index, 1);
};

Lomakkeen lähettämisen käsittely

Lisää Submit Order -syötteen <form>-tagien sisälle ja alareunaan ja lisää onSubmit() klikkauksen käsittelijään alla olevan kaltaisesti:

<form ="this.ordersService.form" (ngSubmit)="onSubmit()"> ...
<button
class="waves-effect waves-light btn col s12"
(click)="onSubmit()">
Submit
</button>
</form>

Luo orders.component:n sisälle tyhjä onSubmit funktio välivaihetta varten. Lisäämme siihen pian tapahtumankäsittelyn. Tässä vaiheessa lomakkeesi pitäisi olla valmis Firebase-tietokantaan kytkemistä varten.

Listauskomponentin asettaminen

Nyt tarvitsemme vain tilan, jossa voimme näyttää kahvitilauksemme tietokannasta. Toistaiseksi asetamme vain telineen html:n.

Navigoi kohtaan order-list.component.html ja luo taulukko, jossa on 3 otsikkoa ja 5 datasolua. Kolmeen ensimmäiseen datasoluun tallennetaan tietokannasta vedetyt arvot ja kahteen viimeiseen tallennetaan lisätoiminnot, joiden avulla voit merkitä tilauksen valmiiksi tai poistaa tilauksen.

Firebasen perustaminen

Mene Firebase-konsoliin ja lisää uusi projekti.

Luo uusi projekti napsauttamalla kohtaa ’Lisää projekti’.

Lisää projektin nimi, hyväksy ehdot ja napsauta kohtaa ’Luo projekti’.

Anna projektillesi nimi ja hyväksy ohjaus-ohjausehdot luodaksesi projektin.

Kun Firebase-projektisi on luotu, näet jotakin tällaista.

Luo tietokanta valitsemalla sivupaneelissa (Develop-kohdan alla) Database (tietokanta) ja napsauttamalla sitten Cloud Firestore -bannerin alla olevaa Create Database (Luo tietokanta) -painiketta.

Napsauta Create database (Luo tietokanta) -painiketta.

Valitse tietoturvasääntöjä varten aloita testitilassa. Voit muokata sitä myöhemmin.

Muista säilyttää tunnistetiedot vain paikallisella koneellasi turvallisuussyistä.

Saat tyhjän Firestore-tietokannan näin.

Firebase Firestoren liittäminen Angulariin

Voidaksesi liittää Angular-sovelluksesi Firebaseen sinun on asennettava firebase ja @angular/fire paketit. Näin saat käyttöösi AngularFireModule ja AngularFirestoreModule. Asenna ne terminaalissa seuraavalla komennolla.

npm i --save firebase @angular/fire

Kytkentöjen toteuttaminen

Palaa takaisin Firebase-verkkokonsoliin ja nappaa konfigurointitiedot Angular-sovelluksessasi käytettäväksi. Nämä löytyvät Project Overviewsivultasi. Se näyttää jotakuinkin tältä:

Tarvitset omia tunnuksiasi. Yllä oleva konfiguraatio ei toimi sinulle.

Kopioi korostettu osa koodista, siirry environment.ts-tiedostoosi (joka sijaitsee environments-kansion sisällä) ja liitä konfiguraation tiedot environment-objektin sisälle firebaseConfig. Katso alla oleva esimerkki:

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"
}
};

Importoi aiemmin asentamasi paketit ja environment.ts-tiedosto app.module.ts:iin.Sinun täytyy alustaa AngularFireModule sillä firebaseConfig:llä, jonka asetukset olet juuri tehnyt. Katso esimerkki alla:

import { environment } from "src/environments/environment";
import { AngularFireModule } from "@angular/fire";
import { AngularFirestoreModule } from "@angular/fire/firestore";
...
@NgModule({
...
imports:
...
})

Firestoren perustaminen palveluun

Importoi AngularFirestore tiedostoon orders.service.ts ja ilmoita se konstruktorissa, jotta palvelusi tietää siitä.

...
import { AngularFirestore } from '@angular/fire/firestore';
...
export class OrdersService {
constructor( private firestore: AngularFirestore ) {}
...
}

Nyt olet valmis aloittamaan tämän Angular + Firebase Firestore -oppaan CRUD-osuuden.

C is for Create

Luodaksesi uuden tietueen upouuteen Firestore-tietokantaasi sinun täytyy kutsua .add() . Teemme tämän orders.service.ts-tiedoston sisällä.

Tehdäksesi tämän sinun on määritettävä collection-nimi ja mitä tietoja haluat työntää tietokantaan. Meidän tapauksessamme se on coffeeOrders .

Alla oleva esimerkkikoodi käyttää lupausta palauttaakseen Firebase-kutsun ja antaa sinun päättää, mitä haluat tehdä sen jälkeen, kun kaikki on tehty.

...
createCoffeeOrder(data) {
return new Promise<any>((resolve, reject) =>{
this.firestore
.collection("coffeeOrders")
.add(data)
.then(res => {}, err => reject(err));
});
}
...

Kutsuaksesi tätä funktiota komponentissasi siirry kohtaan orders.component.ts ja tee onSubmit()-funktion ja toimintokäsittelijän sisällä kutsu createCoffeeOrder() kautta ordersService.

Alhaalla oleva esimerkki tekee jonkin verran tietojen käsittelyä kuvaamalla coffeeOrder-matriisin lomakkeen arvoksi coffeeOrder. Olen myös luonut data-muuttujan rakenneuudistuksen vuoksi (yhdessä sen kanssa, että createCoffeeOrder():een ei tarvitse syöttää massiivisen pitkää nimeä).

...
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*/
});
}
...

Ja viola! Olet nyt luonut tietueen localhost Angular-sovelluksestasi Firestore-tietokantaan.

R is for Read

Voidaksesi näyttää kahvitilaustietosi, tarvitset lukufunktion orders.service.ts:ssäsi. Seuraava koodiesimerkki antaa sinulle kaikki coffeeOrders-kokoelmaasi tallennetut arvot.

...
getCoffeeOrders() {
return
this.firestore.collection("coffeeOrders").snapshotChanges();
}
...

Käyttääksesi tätä funktiota sinun on kutsuttava sitä orders-list.component.ts:stä . Voit tehdä tämän tuomalla OrdersService tiedostoon ja alustamalla sen constructor:ssä.

...
import { OrdersService } from "../shared/orders.service";
...export class OrderListComponent implements OnInit {
constructor(private ordersService:OrdersService){}
...
}

Luo funktio, joka sisältää kutsusi ja alustat sen ngOnInit():ssä kutsuaksesi sitä, kun näkymä ladataan ensimmäisen kerran. Luo coffeeOrders-muuttuja kuvaamaan tietokannastasi subscribe() kautta palautettuja tuloksia. Käytämme tätä iterointiin ja näyttämiseen order-list.component.html

...
ngOnInit() {this.getCoffeeOrders();}
... coffeeOrders; getCoffeeOrders = () =>
this.ordersService
.getCoffeeOrders()
.subscribe(res =>(this.coffeeOrders = res));...

Käyttääksesi coffeeOrders:tä order-list.component.html:ssäsi, käytä *ngFor:tä kierrättääksesi palautettua arraya. Sinun täytyy myös tehdä hieman inceptionia ja tehdä toinen silmukka coffeeOrder-osalle, jotta saat luettelon kahveista jokaiselle asiakkaalle. On olemassa tehokkaampiakin tapoja tehdä tämä, mutta tätä opetusohjelmaa varten katso alla olevaa esimerkkikoodia:

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

Ja siinä se on. Olet nyt kytkenyt lukutoiminnot Angular-sovellukseesi.

U is for Update

Esitettäköön, että haluamme pystyä merkitsemään kahvitilauksen valmiiksi tietokannassa ja tehdä jotain rivikohdalle muutoksen perusteella. Koska snapshot() pitää kirjaa kaikista tapahtuvista muutoksista, sinun ei tarvitse tehdä mitään seurantaa tai kyselyä tietokantaan.

Luomme taulukkoon toisen datasolun, jossa on Google Materialize Iconsin ”check”-kuvake ja kytkemme sen (click)-tapahtumaan, joka kutsuu funktiota markCompleted() toiminnossasi order-list.component.ts. Siirrämme myös tietyn järjestyksen tätä silmukkaa varten.

Asetamme -attribuutin completed-arvolla tietosoluun, jotta se voi dynaamisesti määrittää, haluammeko ’check’-kuvakkeen näkyviin. Olemme alun perin asettaneet tämän arvon false, kun loimme lomakkeen ensimmäisen kerran orders.service.ts .

...
<td ="order.payload.doc.data().completed"
(click)="markCompleted(order)">
<i class="material-icons">check</i>
</td>
...

Inside order-list.component.ts , luo funktio markCompleted(), joka käyttää injektoitua data:a välittääkseen sen funktiolle nimeltä updateCoffeeOrder() orders.service.ts.

...
markCompleted = data =>
this.ordersService.updateCoffeeOrder(data);
...

Inside orders.service.ts luo käsittelyfunktio. Tämä funktio muodostaa yhteyden ja kutsuu Firestore-tietokantaa valitun kokoelman ja asiakirjan id:n perusteella. Tiedämme jo, että kokoelmamme on nimeltään coffeeOrders ja voimme löytää asiakirjan id:n komponenttifunktion kutsusta välitettyjen parametrien perusteella.

.set() asettaa tietyn tietueen mihin tahansa välittämilläsi tiedoilla. .set() ottaa vastaan kaksi parametria – tietosi ja asetusobjektin. Jos käytät merge: true, se tarkoittaa, että päivität vain välitetyn arvo-avain-parin sen sijaan, että korvaisit koko dokumentin sillä, mitä välitit.

...
updateCoffeeOrder(data) {
return
this.firestore
.collection("coffeeOrders")
.doc(data.payload.doc.id)
.set({ completed: true }, { merge: true });
}
...

Nyt kun napsautat näkymässäsi ’tarkista’-kuvaketta, se päivittää tietokantasi ja katoaa, koska -attribuuttisi on nyt asetettu true:ksi.

D is for Delete

Viimeistä toimintoa varten asetamme kaiken samalla tavalla kuin päivitysprosessin – mutta sen sijaan, että päivitämme tietueen, poistamme sen.

Asetetaan toinen datasolu, jossa on klikkaustapahtuman käsittelijä, joka kutsuu deleteOrder()-funktiota. Siirrämme silmukassa order-datan instanssin, kun ’delete_forever’-kuvaketta napsautetaan. Tarvitset tätä asiakirjan id:tä varten. Katso esimerkki alla olevasta koodista:

...
<td ="order.payload.doc.data().completed"
(click)="deleteOrder(order)">
<i class="material-icons">delete_forever</i>
</td>
...

Luo order-list.component.ts-tiedoston sisälle siihen liittyvä funktio, joka kutsuu deleteCoffeeOrder()-funktiota orders.service.ts-tiedoston sisälle.

...
deleteOrder = data => this.ordersService.deleteCoffeeOrder(data);
...

Luo orders.service.ts-tiedoston sisälle deleteCoffeeOrder()-funktio ja käytä injektoitua data:a selvittääksesi, mikä on dokumentin id. Kuten päivitys, sinun on tiedettävä sekä kokoelman nimi että asiakirjan id, jotta voit tunnistaa oikein, minkä tietueen haluat poistaa. Käytä .delete() kertoaksesi Firestorelle, että haluat poistaa tietueen.

...
deleteCoffeeOrder(data) {
return
this.firestore
.collection("coffeeOrders")
.doc(data.payload.doc.id)
.delete();
}
...

Nyt kun napsautat ’delete_forever’ -kuvaketta, Angular-sovelluksesi laukeaa ja käskee Firestorea poistamaan tietyn tietueen. Tietueesi katoaa näkymästä, kun määritetty asiakirja poistetaan.

Loppusovellus

Täältä löydät koko työprojektin Github-arkiston. Sinun täytyy luoda oma Firebase-tietokantasi ja kytkeä se itse päivittämällä konfigurointitiedostot. Koodi itsessään ei ole täydellistä, mutta olen pitänyt sen hyvin minimaalisena, jotta näet, miten Firestore toimii Angular-sovelluksessa ilman, että sinun tarvitsee käydä läpi koodiviidakkoa.

Olen yrittänyt tiivistää opetusohjelmaa niin paljon kuin mahdollista ilman, että siitä puuttuu mitään yksityiskohtia. Se oli kova sekoitus, mutta kuten yllä näkyy, suuri osa siitä on enimmäkseen Angular-koodia eikä juurikaan Firestorea. Firebase Firestore voi tehdä paljon monimutkaisempia kyselyjä, mutta esittelyn vuoksi olen pitänyt sen yksinkertaisena. Teoriassa, jos tietorakenteesi pysyy samana, voit vaihtaa Firestoren pois ja laittaa eri joukon API-yhteyksiä minimaalisella refaktoroinnilla.

Loppusanat

Pidän Firebasea henkilökohtaisesti sen takia, että sillä on helppo luoda nopeasti tuotantokelpoisia projekteja ilman, että tarvitsee luoda kokonainen backend ja palvelin sen tueksi. Kustannuksiltaan se ei ole liian paha ja testiprojektit ovat ilmaisia käynnistää (ja he eivät pakota sinua syöttämään luottokorttitietojasi ennen kuin olet valmis vaihtamaan pakettia). Kaiken kaikkiaan lomakkeen ja sovellusta ympäröivän käyttöliittymän luominen vei enemmän aikaa kuin sovelluksen liittäminen Firestoreen. En ole lisännyt lomakkeen validointia tähän opetusohjelmaan pituuden vuoksi, mutta aion luoda siitä toisen postauksen tulevaisuudessa.

Jätä kommentti