Jak na CRUD v Angularu + Firebase Firestore

CRUD – Create, Read, Update, Delete – čtyři svaté grály databázových akcí. Jsou to jediné věci, které kdy budete potřebovat, abyste s databází udělali něco významného. Jistě, můžete zvyšovat složitost dotazů, ale nakonec se vše smrskne na tyto čtyři akce.

Firebase je cloudový systém vlastněný společností Google, který je vybaven háčky API, prostorem pro ukládání souborů, systémem autentizace a možností hostování. Je to hodně podceňovaný systém, který by se měl více využívat pro prototypování a rychlý vývoj aplikací.

Pokud chcete spustit progresivní webovou aplikaci, ale nemáte zkušenosti s nastavováním serverů, vytvářením rozhraní API a prací s databázemi, pak je Firebase fantastickou volbou pro frontendové vývojáře, kteří se mohou cítit izolovaní a zahlcení obrovským kopcem informací, které musí zpracovat, aby vůbec mohli spustit svou aplikaci.

Nebo pokud máte jen málo času, Firebase vám může zkrátit hodiny vývoje téměř na polovinu, takže se můžete soustředit na uživatelské prostředí a implementaci těchto toků uživatelského rozhraní. Je také dostatečně flexibilní na to, abyste mohli v případě potřeby migrovat svou front end aplikaci a použít jinou databázi.

Tady je stručný návod, jak implementovat CRUD akce pomocí Angularu a Firebase.

Jedná se o holou kostru aplikace pro objednávání kávy, kde můžete přidávat objednávky (create), vypisovat objednávky z databáze (read), označovat objednávku jako dokončenou (update) a odstraňovat objednávku (delete).

Účelem tohoto návodu je pomoci vám začít s Firebase Firestore a zjistit, jak snadné je připojit se ke službě vlastněné společností Google a začít ji používat. Nejedná se o reklamu společnosti Google (nedostávám za to od ní žádné výpalné), ale pouze o ukázku toho, jak si Angular hraje s databází.

Aplikace, kterou budeme vytvářet, není dokonalá. Chybí tam věci jako validace dat a milion možných funkcí, které mohu také přidat. Ale o to tady nejde. Jde o to, abychom ji co nejrychleji nastavili v Angularu a zprovoznili ji s živou databází.

Takže dost úvodu – tady je procházka kódem.

Počáteční nastavení

Nastavte si aplikaci Angular přes Angular CLI. Pokud ještě nemáte Angular CLI, můžete se o něm dozvědět více zde.

Zkrátka jednoduše spusťte tyto příkazy v terminálu v adresáři, kde chcete mít svou aplikaci Angular. Zde jsou příkazy a jejich funkce.

npm install -g @angular/cli

Instalace Angular CLI v terminálu, pokud jej ještě nemáte.

ng new name-of-app-here

Vytvoří nový projekt Angular s použitím nejnovější dostupné verze Angularu.

cd name-of-app-here

Při vytvoření nového projektu pomocí Angular CLI se vám vytvoří nová složka projektu. cd Přes terminál se dostanete do této složky.

ng serve

Tímto spustíte a spustíte svůj projekt Angular.

Nastavení Angularu

Pro tuto aplikaci Angular vytvoříme celkem 3 nové části – orders, order-list a shared/ordersService. První dvě jsou komponenty, které budou obsahovat rozhraní aplikace, a služba shared/orders, která bude udržovat všechna volání rozhraní Firebase API pohromadě.

Pro vytvoření potřebných souborů spusťte následující příkazy:

ng g c orders

g znamená generate a c znamená component. Poslední část příkazu je název vašeho souboru, který se pro výše uvedený případ nazývá orders. Pro dosažení stejného efektu můžete také použít ng generate component orders.

Vytvořte další komponentu pro seznam objednávek pomocí následujícího příkazu:

ng g c order-list

A konečně pro službu použijte s místo c jako níže:

ng g s shared/orders

Tím vytvoříte soubor orders.service.ts ve složce s názvem shared. Nezapomeňte přidat orders.service.ts do app.module.ts, protože to za vás nebude provedeno automaticky jako u komponent. Můžete to udělat prostřednictvím import a přidáním do seznamu providers takto:

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

The CSS

Pro tento tutoriál budeme používat Materialize CSS, aby naše výsledná aplikace vypadala lépe než výchozí css. Není to nutné a pokud chcete, můžete použít libovolný framework CSS nebo vlastní CSS. Podrobnosti o Materialize CSS si můžete prohlédnout zde.

Budeme také používat hmotné ikony Googlu jako tlačítka pro označení objednávky kávy jako dokončené nebo pro odstranění objednávky.

Jedním ze způsobů, jak to implementovat, je mít níže uvedený kód přímo nad značkou </head> v souboru index.html umístěném ve složce src.

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

Počáteční kód zobrazení Angular

V souboru app.component.html odstraňte veškerý startovací kód. Budeme pro něj používat vlastní obsah.

Připojíme se k právě vytvořené komponentě a zobrazíme je na obrazovce pomocí selektorů app-orders a app-order-list. Za tímto účelem napíšeme následující kód:

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

Třídy container, row, col a s6 jsou součástí mřížkového systému Materialize CSS. Všechny třídy, které uvidíte ve zbytku tohoto návodu, pocházejí z Materialize CSS, pokud není uvedeno jinak.

Nastavení formuláře

Pro nastavení formulářů importujte ReactiveFormsModule v app.module.ts a nezapomeňte jej importovat v poli imports.

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

Vnitř orders.service.ts importujte FormControl a FormGroup z @angular/formsa vytvořte nový formulář mimo constructor, kde můžete nastavit vlastnosti formuláře, jak je uvedeno níže:

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

Tyto hodnoty použijeme k uložení do Firebase o něco později v tomto návodu.

Použití formuláře uvnitř komponenty

importTřídu OrdersService vložte do své komponenty, abyste mohli použít objekt uvnitř komponenty a poté vytvořit objekt této třídy uvnitř constructor.

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

Uvnitř své orders.component.html použijte direktivu formGroup pro odkaz na objekt formuláře vytvořený v OrdersService. Každá formControlName odkazuje na jména, která jsme použili v formGroup vytvořené uvnitř třídy OrdersService. To umožní přidruženému kontroléru používat proměnné zadané do formuláře. Viz kód níže:

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

V orders.component.ts nastavíme pole off coffee pro procházení smyčkou na našem orders.component.html. Teoreticky byste to mohli nastavit i uvnitř databáze Firestore, provést volání kolekce a pak ji použít. Ale pro účely délky to nastavíme jako lokální pole. Uvnitř třídy OrdersComponent nastavte následující pole:

coffees = ;

Ve značce orders.component.html a uvnitř značky <form> jej procházejte ve smyčce pomocí *ngFor s obsluhou akce (click) a přidávejte nové kávy do objednávky. Přímo pod ním zobrazíme seznam objednávek s možností odebrat jednotlivé kávy následujícím způsobem:

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

Uvnitř orders.component vytvoříte prázdné pole pro uložení objednávky kávy, pomocí funkce addCoffee() přidáte nové kávy a pomocí removeCoffee() odeberete nápoj ze seznamu objednávek.

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

Obsluha odeslání formuláře

Vnitř a na konec značek <form> přidejte vstup Odeslat objednávku a přidejte onSubmit() do obsluhy kliknutí, jak je uvedeno níže:

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

Ve svém orders.component vytvořte prázdnou funkci onSubmit pro mezidobí. Brzy do ní přidáme obsluhu událostí. V tuto chvíli by měl být váš formulář připraven k připojení k databázi Firebase.

Nastavení komponenty výpisu

Teď už jen potřebujeme prostor pro zobrazení našich objednávek kávy z databáze. Zatím jen nastavíme lešení html.

Přejděte do svého order-list.component.html a vytvořte tabulku se 3 nadpisy a 5 datovými buňkami. První 3 datové buňky budou obsahovat hodnoty vytažené z databáze a poslední 2 budou obsahovat další funkce, které vám umožní označit objednávku jako dokončenou nebo ji smazat.

Nastavení Firebase

Přejděte do konzole Firebase a přidejte nový projekt.

Klikněte na „Přidat projekt“ a vytvořte nový projekt.

Přidejte název projektu, přijměte podmínky a klikněte na vytvořit projekt.

Přidělte projektu název a přijměte podmínky kontroléru-kontroléru, abyste mohli projekt vytvořit.

Po vytvoření projektu Firebase se zobrazí něco takového.

Vytvořte databázi výběrem možnosti Databáze na bočním panelu (umístěném v části Vyvíjet) a poté klikněte na možnost Vytvořit databázi pod bannerem Cloud Firestore.

Klikněte na „Vytvořit databázi“

Zvolte spuštění v testovacím režimu pro bezpečnostní pravidla. Později je můžete upravit.

Zapomeňte si z bezpečnostních důvodů ponechat přihlašovací údaje pouze na svém lokálním počítači.

Zobrazí se vám prázdná databáze Firestore, jako je tato.

Připojení Firebase Firestore k Angularu

Pro připojení aplikace Angular k Firebase budete potřebovat nainstalovat balíčky firebase a @angular/fire. Tím získáte přístup k souborům AngularFireModule a AngularFirestoreModule. Pomocí následujícího příkazu v terminálu je nainstalujte.

npm i --save firebase @angular/fire

Implementace konektorů

Přejděte zpět do webové konzoly Firebase a převezměte konfigurační údaje, které chcete použít ve své aplikaci Angular. Ty se nacházejí na stránce Přehled projektu. Vypadá nějak takto:

Musíte použít vlastní přihlašovací údaje. Výše uvedená konfigurace vám nebude fungovat.

Zkopírujte zvýrazněnou část kódu, přejděte do svého souboru environment.ts (umístěného uvnitř složky environments) a vložte údaje konfigurace dovnitř objektu environment jako firebaseConfig. Příklad viz níže:

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

Importujte balíčky, které jste nainstalovali dříve, a soubor environment.ts do svého app.module.ts. budete muset inicializovat AngularFireModule pomocí firebaseConfig, pro který jste právě provedli nastavení. Viz příklad níže:

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

Nastavení Firestore ve službě

Importujte AngularFirestore do svého souboru orders.service.ts a deklarujte jej v konstruktoru, aby o něm vaše služba věděla.

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

Teď máte vše nastaveno a můžete se pustit do CRUD části tohoto návodu Angular + Firebase Firestore.

C znamená Create

Pro vytvoření nového záznamu ve vaší zbrusu nové databázi Firestore budete muset zavolat .add() . Uděláme to uvnitř souboru orders.service.ts.

K tomu budete muset zadat název collection a jaká data chcete do databáze odeslat. V našem případě je to coffeeOrders .

Následující příklad kódu používá slib, který vrátí volání Firebase, a pak vás nechá rozhodnout, co chcete udělat, až bude vše hotovo.

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

Chcete-li tuto funkci zavolat ve své komponentě, přejděte na orders.component.ts a uvnitř funkce onSubmit() a obsluhy akce proveďte volání createCoffeeOrder() přes ordersService.

Následující příklad provádí určité zpracování dat mapováním pole coffeeOrder na hodnotu formuláře coffeeOrder. Vytvořil jsem také proměnnou data pro účely destrukce (spolu s tím, že nemusím do createCoffeeOrder() předávat masivně dlouhé jméno).

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

A viola! Nyní jste vytvořili záznam z vaší aplikace Angular na localhostu do databáze Firestore.

R is for Read

Pro zobrazení dat o objednávkách kávy budete potřebovat funkci read ve vašem orders.service.ts. Následující příklad kódu vám poskytne všechny hodnoty uložené ve vaší kolekci coffeeOrders.

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

Pro použití této funkce ji budete muset zavolat z vašeho orders-list.component.ts . To můžete udělat tak, že do souboru importujete OrdersService a inicializujete ji v constructor.

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

Vytvořte funkci, která bude obsahovat vaše volání, a inicializujte ji v ngOnInit(), abyste ji zavolali při prvním načtení zobrazení. Vytvořte proměnnou coffeeOrders pro mapování vrácených výsledků z vaší databáze prostřednictvím subscribe(). Budeme ji používat k iteraci a zobrazení v order-list.component.html

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

Pro použití coffeeOrders ve vašem order-list.component.html , použijte *ngFor k procházení vráceného pole ve smyčce. Musíte také udělat trochu inception a udělat další smyčku pro část coffeeOrder, abyste získali seznam káv pro každého zákazníka. Existují efektivnější způsoby, jak to udělat, ale pro tento návod se podívejte na příklad kódu níže:

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

A tady to máte. Nyní jste do své aplikace Angular připojili možnosti čtení.

U je pro aktualizaci

Řekněme, že chceme mít možnost označit objednávku kávy v databázi jako dokončenou a na základě této změny provést něco s položkou. Protože snapshot() sleduje všechny změny, ke kterým dojde, nemusíte provádět žádné sledování ani dotazování do databáze.

Vytvoříme v naší tabulce další datovou buňku s ikonou „check“ z Google Materialize Icons a připojíme ji k události (click), která bude volat funkci markCompleted() ve vašem order-list.component.ts. Budeme také předávat konkrétní pořadí pro tuto smyčku.

Datové buňce nastavíme atribut s hodnotou completed, aby mohla dynamicky určit, zda chceme mít zobrazenou ikonu ‚check‘. Původně jsme tuto hodnotu nastavili jako false při prvním vytvoření formuláře v orders.service.ts .

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

Inside order-list.component.ts , vytvořte funkci markCompleted(), která použije injektovaný data pro předání funkci s názvem updateCoffeeOrder() v orders.service.ts.

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

Inside orders.service.ts vytvořte obslužnou funkci. Tato funkce se připojí a zavolá databázi Firestore na základě vybrané kolekce a id dokumentu. Již víme, že naše kolekce se jmenuje coffeeOrders a id dokumentu zjistíme na základě parametrů předaných z volání funkce komponenty.

.set() nastaví konkrétní záznam s jakýmikoliv předanými údaji. Funkce .set() přijímá dva parametry – vaše data a objekt nastavení. Pokud použijete merge: true, pak to znamená, že aktualizujete pouze předanou dvojici hodnota-klíč, a ne že nahradíte celý dokument tím, co jste předali.

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

Nyní, když kliknete na ikonu „zkontrolovat“ ve vašem zobrazení, aktualizuje se databáze a zmizí, protože váš atribut je nyní nastaven na true.

D is for Delete

Pro závěrečnou akci nastavíme vše podobně jako při aktualizaci – ale místo aktualizace záznamu jej vymažeme.

Nastavte další datovou buňku s obsluhou události kliknutí, která zavolá funkci deleteOrder(). Ve smyčce budeme předávat instanci dat order při kliknutí na ikonu ‚delete_forever‘. To budete potřebovat pro identifikaci dokumentu. Příklad viz kód níže:

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

Uvnitř souboru order-list.component.ts vytvořte přidruženou funkci, která zavolá funkci deleteCoffeeOrder() uvnitř souboru orders.service.ts .

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

Uvnitř souboru orders.service.ts vytvořte funkci deleteCoffeeOrder() a pomocí data injected zjistěte, jaké je id dokumentu. Stejně jako u aktualizace potřebujete znát jak název kolekce, tak id dokumentu, abyste správně určili, který záznam chcete smazat. Pomocí .delete() řekněte Firestore, že chcete odstranit záznam.

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

Nyní, když kliknete na ikonu ‚delete_forever‘, vaše aplikace Angular se spustí a řekne Firestore, aby odstranil konkrétní záznam. Váš záznam zmizí ze zobrazení po smazání zadaného dokumentu.

Finální aplikace

Úložiště Github pro celý pracovní projekt najdete zde. Budete si muset vytvořit vlastní databázi Firebase a připojit ji sami aktualizací konfiguračních souborů. Samotný kód není dokonalý, ale zachoval jsem ho v minimální míře, abyste viděli, jak Firestore funguje v aplikaci Angular, aniž byste se museli probírat džunglí kódu.

Snažil jsem se návod co nejvíce zestručnit, aniž bych vynechal nějaké detaily. Byl to těžký mix, ale jak můžete vidět výše, hodně z toho je hlavně kód Angularu a málo Firestore. Firebase Firestore umí mnohem složitější dotazy, ale pro demonstrační účely jsem to ponechal jednoduché. Teoreticky, pokud struktura dat zůstane stejná, můžete Firestore vyměnit a vložit jinou sadu připojení API s minimálními nároky na refaktoring.

Slova na závěr

Osobně mám Firebase rád kvůli tomu, jak snadno lze rychle vytvořit projekty připravené k produkci bez nutnosti vytvářet celý backend a server pro jeho podporu. Cenově na tom není špatně a zkušební projekty jsou zdarma k zavedení (a nenutí vás zadávat údaje o kreditní kartě, dokud nejste připraveni změnit plán). Celkově zabralo více času vytvoření formuláře a rozhraní kolem aplikace než samotný akt připojení aplikace k Firestore. Kvůli délce jsem do tohoto návodu nepřidal validaci formuláře, ale v budoucnu o ní vytvořím další příspěvek.

Napsat komentář