Angular 8 Firestore tutorial with CRUD application example – @angular/fire

[no_toc]Tutorial: Angular 8 Firestore tutorial with CRUD application example – @angular/fire

Cloud Firestore helps us store data in the cloud. It supports offline mode so our app will work fine (write, read, listen to, and query data) whether device has internet connection or not, it automatically fetches changes from our database to Firebase Server. We can structure data in our ways to improve querying and fetching capabilities. This tutorial shows you how to work with Firebase Firestore along with an Angular app that can do CRUD Operations.

Related Posts:
Angular 8 Firebase tutorial: Integrate Firebase into Angular 8 App with @angular/fire
Angular 8 Firebase CRUD operations with @angular/fire
Angular 8 – Upload/Display/Delete files to/from Firebase Storage using @angular/fire

Technology – Angular 8 Firestore CRUD

– Angular 8
– @angular/fire 5.1.2
– firebase 5.10.1

Video – Angular 8 Firestore CRUD

Set up the Firebase Project & Install @angular/fire

Please visit this post to know step by step.

The only different for this tutorial is that, we use Firebase Firestore instead of Firebase Realtime Database:

angular-8-firestore-turorial-create-database

Firebase CRUD operations for Object

– Create an object binding/ Retrieve:

item: AngularFirestoreDocument<any>;
// db: AngularFirestore
this.item = db.doc('item');

// or
Observable<any> item = db.doc('item').valueChanges();

– Create Operation:


// db: AngularFirestore
const itemRef = db.doc('item');

// set() for destructive updates
itemRef.set({ name: 'ozenero'});

– Update operation:

// db: AngularFirestore
const itemRef = db.doc('item');
itemRef.update({ url: 'ozenero.com'});

– Delete Operation:


// db: AngularFirestore
const itemRef = db.doc('item');
itemRef.delete();

Firebase CRUD operations for List of Objects

1. Create a list binding/ Retrieve:
– Returns an Observable of data as a synchronized array of JSON objects without snapshot metadata. It is simple to render to a view:

items: Observable<any[]>;
// db: AngularFirestore
this.items = db.collection('items').valueChanges();

– Returns an Observable of data as a synchronized array of DocumentChangeAction<>[] with metadata (the underlying data reference and snapshot id):

items: Observable<any[]>;
// db: AngularFirestore
this.items = db.collection('items').snapshotChanges();

– Create Operation:


// db: AngularFirestore
const itemsRef = db.collection('items');
itemsRef.add({ site: 'ozenero.com' });

– Update Operation:


// set(): destructive update
// delete everything currently in place, then save the new value
const itemsRef = db.collection('items'); // db: AngularFirestore
itemsRef.doc(key).set({ url: 'gkz.com' });

// update(): non-destructive update
// only updates the values specified
const itemsRef = db.collection('items'); // db: AngularFirestore
itemsRef.doc(key).update({ url: 'ozenero.com' });

– Delete Operation:

// db: AngularFirestore
const itemsRef = db.collection('items');
itemsRef.doc(key).delete();

// delete entire list
itemsRef.get().subscribe(
      querySnapshot => {
        querySnapshot.forEach((doc) => {
          doc.ref.delete();
        });
      },
      error => {
        console.log('Error: ', error);
      });

Configure offline persistence

By default, offline persistence is disabled. To enable it, call enablePersistence() method:

firebase.firestore().enablePersistence()
  .catch(err => {
    if (err.code == 'failed-precondition') {
      // only be enabled in one tab at a a time (multiple tabs open)
    } else if (err.code == 'unimplemented') {
      // browser does not support all of the features required to enable persistence
    }
  });

Project Goal – Angular 8 Firestore CRUD

We will build an Angular 8 Firebase App using @angular/fire that can:
– add/remove Customer
– show all Customers
– update Customer’s status
to Firebase Firestore.

angular-8-firestore-turorial-demo-goal

Project Structure

angular-8-firestore-turorial-project-structure

environment.ts configure information to connect with Firebase Project.
customer.ts defines Customer class data model.
customer.service.ts defines CustomerService that uses @angular.fire to interact with Firebase.
– 3 Angular components:
+ create-customer for creating new item.
+ customer-details shows item detais
+ customers-list contains list of items, this is parent component of customer-details
app-routing.module.ts defines how we route Angular components.
app.module.ts declares and imports necessary environment & modules.

Set up the Firebase Project & Install @angular/fire

Please visit this post to know step by step.

angular-8-firestore-turorial-create-database

Add Firebase config to environments variable

Open /src/environments/environment.ts, add your Firebase configuration that we have saved when Popup window was shown:

export const environment = {
  ...
  firebase: {
    apiKey: 'xxx',
    authDomain: 'gkz-angular-firebase.firebaseapp.com',
    databaseURL: 'https://gkz-angular-firebase.firebaseio.com',
    projectId: 'gkz-angular-firebase',
    storageBucket: 'gkz-angular-firebase.appspot.com',
    messagingSenderId: '123'
  }
};

angular-8-firebase-tutorial-integrate-angular-fire-popup

Create Service & Components

Run the commands below:
ng g s customers/Customer
ng g c customers/CustomerDetails
ng g c customers/CustomersList
ng g c customers/CreateCustomer

Setup @NgModule

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { FormsModule } from '@angular/forms';

import { AngularFireModule } from '@angular/fire';
import { AngularFirestoreModule, FirestoreSettingsToken } from '@angular/fire/firestore';
import { environment } from '../environments/environment';

import { AppComponent } from './app.component';
import { CustomerDetailsComponent } from './customers/customer-details/customer-details.component';
import { CustomersListComponent } from './customers/customers-list/customers-list.component';
import { CreateCustomerComponent } from './customers/create-customer/create-customer.component';

@NgModule({
  declarations: [
    AppComponent,
    CustomerDetailsComponent,
    CustomersListComponent,
    CreateCustomerComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    AppRoutingModule,
    AngularFireModule.initializeApp(environment.firebase),
    AngularFirestoreModule
  ],
  providers: [{ provide: FirestoreSettingsToken, useValue: {} }],
  bootstrap: [AppComponent]
})
export class AppModule { }

Create Model Class

customer.ts


export class Customer {
  key: string;
  name: string;
  age: number;
  active = true;
}

The key field is important for updating item.

Create Data Service

customer.service.ts


import { Injectable } from '@angular/core';
import { AngularFirestore, AngularFirestoreCollection } from '@angular/fire/firestore';
import { Customer } from './customer';

@Injectable({
  providedIn: 'root'
})
export class CustomerService {

  private dbPath = '/customers';

  customersRef: AngularFirestoreCollection<Customer> = null;

  constructor(private db: AngularFirestore) {
    this.customersRef = db.collection(this.dbPath);
  }

  createCustomer(customer: Customer): void {
    this.customersRef.add({...customer});
  }

  updateCustomer(key: string, value: any): Promise<void> {
    return this.customersRef.doc(key).update(value);
  }

  deleteCustomer(key: string): Promise<void> {
    return this.customersRef.doc(key).delete();
  }

  getCustomersList(): AngularFirestoreCollection<Customer> {
    return this.customersRef;
  }

  deleteAll() {
    this.customersRef.get().subscribe(
      querySnapshot => {
        querySnapshot.forEach((doc) => {
          doc.ref.delete();
        });
      },
      error => {
        console.log('Error: ', error);
      });
  }
}

Create Component for item Details

customer-details.component.ts

import { Component, OnInit, Input } from '@angular/core';
import { CustomerService } from '../customer.service';
import { Customer } from '../customer';

@Component({
  selector: 'app-customer-details',
  templateUrl: './customer-details.component.html',
  styleUrls: ['./customer-details.component.css']
})
export class CustomerDetailsComponent implements OnInit {

  @Input() customer: Customer;

  constructor(private customerService: CustomerService) { }

  ngOnInit() {
  }

  updateActive(isActive: boolean) {
    this.customerService
      .updateCustomer(this.customer.key, { active: isActive })
      .catch(err => console.log(err));
  }

  deleteCustomer() {
    this.customerService
      .deleteCustomer(this.customer.key)
      .catch(err => console.log(err));
  }

}

customer-details.component.html

<div *ngIf="customer">
  <div>
    <label>First Name: </label> {{customer.name}}
  </div>
  <div>
    <label>Age: </label> {{customer.age}}
  </div>
  <div>
    <label>Active: </label> {{customer.active}}
  </div>

  <span class="button is-small btn-primary" *ngIf='customer.active' (click)='updateActive(false)'>Inactive</span>

  <span class="button is-small btn-primary" *ngIf='!customer.active' (click)='updateActive(true)'>Active</span>

  <span class="button is-small btn-danger" (click)='deleteCustomer()'>Delete</span>

  <hr />
</div>

Create Component to show List of Items

customers-list.component.ts

import { Component, OnInit } from '@angular/core';
import { CustomerService } from '../customer.service';
import { map } from 'rxjs/operators';

@Component({
  selector: 'app-customers-list',
  templateUrl: './customers-list.component.html',
  styleUrls: ['./customers-list.component.css']
})
export class CustomersListComponent implements OnInit {

  customers: any;

  constructor(private customerService: CustomerService) { }

  ngOnInit() {
    this.getCustomersList();
  }

  getCustomersList() {
    this.customerService.getCustomersList().snapshotChanges().pipe(
      map(changes =>
        changes.map(c =>
          ({ key: c.payload.doc.id, ...c.payload.doc.data() })
        )
      )
    ).subscribe(customers => {
      this.customers = customers;
    });
  }

  deleteCustomers() {
    this.customerService.deleteAll();
  }

}

In the code above, we use snapshotChanges() with RxJS map() operator to get the id of each item and assign to key field.

customers-list.component.html:

<h1>Customers</h1>
<div *ngFor="let customer of customers" style="width: 300px;">
  <app-customer-details [customer]='customer'></app-customer-details>
</div>
<div style="margin-top:20px;">
  <button type="button" class="button btn-danger" (click)='deleteCustomers()'>Delete All</button>
</div>

We pass each customer item data to customer-details component.

Create Component to save item

create-customer.component.ts

import { Component, OnInit } from '@angular/core';
import { FormsModule } from '@angular/forms';

import { Customer } from '../customer';
import { CustomerService } from '../customer.service';

@Component({
  selector: 'app-create-customer',
  templateUrl: './create-customer.component.html',
  styleUrls: ['./create-customer.component.css']
})
export class CreateCustomerComponent implements OnInit {

  customer: Customer = new Customer();
  submitted = false;

  constructor(private customerService: CustomerService) { }

  ngOnInit() {
  }

  newCustomer(): void {
    this.submitted = false;
    this.customer = new Customer();
  }

  save() {
    this.customerService.createCustomer(this.customer);
    this.customer = new Customer();
  }

  onSubmit() {
    this.submitted = true;
    this.save();
  }

}

create-customer.component.html:

<h3>Create Customer</h3>
<div [hidden]="submitted" style="width: 300px;">
  <form (ngSubmit)="onSubmit()">
    <div class="form-group">
      <label for="name">Name</label>
      <input type="text" class="form-control" id="name" required [(ngModel)]="customer.name" name="name">
    </div>

    <div class="form-group">
      <label for="age">Age</label>
      <input type="text" class="form-control" id="age" required [(ngModel)]="customer.age" name="age">
    </div>

    <button type="submit" class="btn btn-success">Submit</button>
  </form>
</div>

<div [hidden]="!submitted">
  <h4>You submitted successfully!</h4>
  <button class="btn btn-success" (click)="newCustomer()">Add</button>
</div>

Define App Routing Module

app-routing.module.ts

import { CreateCustomerComponent } from './customers/create-customer/create-customer.component';
import { CustomersListComponent } from './customers/customers-list/customers-list.component';

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

const routes: Routes = [
  { path: '', redirectTo: 'customers', pathMatch: 'full' },
  { path: 'customers', component: CustomersListComponent },
  { path: 'add', component: CreateCustomerComponent }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

app.component.html:


<div class="container-fluid">
  <div style="color: blue;">
    <h1>{{title}}</h1>
    <h3>{{description}}</h3>
  </div>

  <nav>
    <a routerLink="customers" class="btn btn-primary active" role="button" routerLinkActive="active">Customers</a>
    <a routerLink="add" class="btn btn-primary active" role="button" routerLinkActive="active">Add</a>
  </nav>
  <router-outlet></router-outlet>
</div>

To understand how to use Angular Routing Module, please visit:
How to work with Angular Routing – Spring Boot + Angular 4

Run & Check Result

– Run Angular App with command: npm start.
– Open browser with url http://localhost:4200/.

angular-8-firestore-turorial-result-browser

Result in Firestore console:

angular-8-firestore-turorial-result-firestore-console

Source Code

Angular8Firestore

Conclusion

Now we’ve known how to connect Angular App with Firebase Firestore, how to make CRUD operations with Firestore for Object or List of Objects. We also created an Angular 8 Application that can save, receive, update, delete items with Firestore Database.

Happy learning!

9 thoughts on “Angular 8 Firestore tutorial with CRUD application example – @angular/fire”

  1. Thanks a lot, its really helpful but i have been trying to update record using a form but to no avail. I need help please, i am a novice of angular.

  2. Hi I am getting the following error while serving the application

    This likely means that the library (@angular/fire/firestore) which declares AngularFirestore has not been processed correctly by ngcc, or is not compatible with Angular Ivy. Check if a newer version of the library is available, and update if so. Also consider checking with the library’s authors to see if
    the library is expected to be compatible with Ivy.

  3. Excellent beat ! I would like to apprentice at the same time as you amend your site, how could i subscribe for a weblog web site? The account aided me a acceptable deal. I have been tiny bit familiar of this your broadcast offered vibrant clear concept

  4. Thanks for the advice on credit repair on all of this blog. Some tips i would advice people is to give up the particular mentality that they buy right now and fork out later. Being a society most of us tend to try this for many issues. This includes family vacations, furniture, and also items we wish. However, you need to separate your current wants from all the needs. If you are working to improve your credit score actually you need some trade-offs. For example you can shop online to save money or you can look at second hand suppliers instead of pricey department stores to get clothing.

  5. I know this if off topic but I’m looking into starting my own blog and was curious what all is required to get setup? I’m assuming having a blog like yours would cost a pretty penny? I’m not very internet smart so I’m not 100 certain. Any suggestions or advice would be greatly appreciated. Many thanks

  6. Great blog here! Additionally your web site rather a lot up fast! What host are you using? Can I am getting your associate hyperlink in your host? I wish my web site loaded up as fast as yours lol

Leave a Reply

Your email address will not be published. Required fields are marked *