Vue.js Firestore example - Vue.js CRUD serverless with Firebase Cloud Firestore
vue
6
firestore
7
CRUD
27
firebase
21
Male avatar

loveprogramming viết ngày 05/03/2021

https://grokonez.com/frontend/vue-js/vue-js-firestore-example-vue-js-crud-serverless-with-firebase-cloud-firestore

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 a Vue.js app that can do Firebase Firestore CRUD Operations.

Related Post: Vue.js CRUD example – a simple Note App

Vue.js Firestore example Overview

Goal

Our Vue App can help us write new Notes, then it displays a list of Notes and each Note page (containing title and content) can be modified easily. This App also supports offline mode - we can create/update/delete Note without internet connection.

vuejs-firestore-example-crud-note-app-overview

And, of course, this App interacts with Firebase Firestore as backend infrastructure:

vuejs-firestore-example-note-app-firebase-firestore-result

Demo

Project Structure

vuejs-firestore-example-crud-note-app-project-structure

We have 3 components:

  • App.vue holds all of the other components.
  • NotesList.vue contains all of notes in a List with + Note button.
  • Note.vue display a single Note in the List that allows us to create new Note or edit current Note.

    Technologies

  • Vue CLI 3.0.1
  • Vue 2.5.17
  • Firebase SDK for Javascript 5.4.2

    Practice

    Setup Vue Project

    Install vue-cli

    For use Vue CLI anywhere, run command: npm install -g vue-cli

    Init Project

    Point cmd to the folder you want to save Project folder, run command: npm create vue-note-app

You will see 2 options, choose default:

vuejs-firestore-example-note-app-project-setup

Install Firebase SDK

Run command: npm install firebase
Once the process is done, you can see firebase in package.json:

"dependencies": {
  "firebase": "^5.4.2",
  "vue": "^2.5.17"
},

Setup Firebase Project

Create Project

Go to Firebase Console, login with your Google Account, then click on Add Project.

Enter Project name, select Location:

vuejs-firestore-example-note-app-add-firebase-project

Then press CREATE PROJECT.

Config Rules for Firebase Cloud Firestore

On the left tab, click on Database.
Choose Cloud Firestore. Click on Create Database, a window is shown, choose Start in test mode:

vuejs-firestore-example-note-app-add-firebase-database-rule

This action is equivalent to set Database Rules:

service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write;
    }
  }
}

Child Components

To understand child Components' code, please read description in the tutorial:
Vue.js CRUD example – a simple Note App

Note Component

components/Note.vue

<template>
   <div class="note">
        <div v-if="note">
            <h3>Note</h3>
            <div class="form-group">
                <input class="form-control" type="text" v-model="note.title" placeholder="Title" />
            </div>
            <div class="form-group">
                <textarea class="form-control" v-model="note.content" placeholder="Content"></textarea>
            </div>
            <button class="btn btn-danger" @click="removeNote()">Remove</button>
            <button class="btn btn-success" @click="saveNote()">Save</button>
        </div>
        <div v-else>
            <h5>Please create new Note...</h5>
        </div>
        
    </div> 
</template>

<script>
export default {
  name: "Note",
  props: ["note"],
  methods: {
    saveNote() {
      this.$emit("app-saveNote");
    },
    removeNote() {
      this.$emit("app-removeNote");
    }
  }
};
</script>

<style>
.note {
  margin: 20px;
}
</style>

NotesList Component

components/NotesList.vue

<template>
    <div class="list">
        <h3>List</h3>
        <ul class="list-group">
            <li class="list-group-item"
                v-for="(note, index) in notes"
                :key="note.index"
                :class="{ 'active': index === activeNote}"
                @click="changeNote(index)"
                >
                <div>{{ note.title }}</div>
            </li>
        </ul>
        <button @click="addNote()" class="btn btn-info">+ Note</button>
    </div>
</template>

<script>
export default {
  name: "NoteList",
  props: ["notes", "activeNote"],
  methods: {
    changeNote(index) {
      this.$emit("app-changeNote", index);
    },
    addNote() {
      this.$emit("app-addNote");
    }
  }
};
</script>

<style>
.list {
  margin: 20px;
}
</style>

App Component

In this Component, we have to do these things:

  • import Firebase, initialize Firestore Database, then make reference to the Database Collection.
  • import and interact 2 components above (NotesList and Note).
  • use Firebase SDK function to do CRUD operations and listen to the Firestore events (create, delete, update).

Import Firebase Firestore

Go to Firebase Console, choose the Project that we have created before -> Project Overview.
Click on Web App icon:

vuejs-firestore-example-note-app-add-firebase-web-app

A Popup will be shown:

vuejs-firestore-example-note-app-add-firebase-config-project

Copy the code and paste in App.vue, don't forget to import, initialize Firebase and create Reference to the Notes Collection:

import firebase from "firebase/app";
import "firebase/firestore";

var config = {
  apiKey: "AIzaSyD7Lw-BWAP1jJUv_Kf3BmZWAxShItPXkug",
  authDomain: "grokonez-vue-firebase.firebaseapp.com",
  databaseURL: "https://grokonez-vue-firebase.firebaseio.com",
  projectId: "grokonez-vue-firebase",
  storageBucket: "grokonez-vue-firebase.appspot.com",
  messagingSenderId: "682969700542"
};
firebase.initializeApp(config);

const database = firebase.firestore();
database.settings({
  timestampsInSnapshots: true
});

database.enablePersistence();

const noteCollection = database.collection("notes");

Import Child Components

We define the data() function that returns array of Notes (notes[]) and a number for current Note's index in the array (index).

<template>
    <div id="app">
        <NotesList 
          @app-addNote="addNote"
          @app-changeNote="changeNote"
          :notes="notes"
          :activeNote="index" />
        <Note
          @app-saveNote="saveNote"
          @app-removeNote="removeNote"
          :note="notes[index]" />
    </div>
</template>

export default {
  name: "app",
  components: {
    NotesList,
    Note
  },
  data: () => ({
    notes: [],
    index: 0
  }),
  ...
}

Please remember that we have used $emit in 2 child Components before:

  • Note Component: $emit("app-removeNote") & $emit("app-saveNote")
  • NotesList Component: $emit("app-changeNote", index) & $emit("app-addNote")

Now we have 4 event handlers with @app- prefix that point to corresponding methods inside App component:

export default {
  ...
  methods: {
    addNote() {
      // ...
    },
    saveNote() {
      // ...
    },
    changeNote(index) {
      // ...
    },
    removeNote() {
      // ...
    }
  }
};

Do Firebase Firestore CRUD Operations

  • Create Note using collectionRef.add({data}):
    
    noteCollection.add(note);
    
  • Update Note using nodeRef.update({data}):
    noteCollection.doc(note.id).update({
    title: note.title,
    content: note.content
    });
  • Delete Note using nodeRef.delete():
    
    noteCollection.doc(id).delete();
  • Read list of Notes using collectionRef.get().then(function(snapshot)):
    noteCollection.get().then(snapshot => {
    snapshot.forEach(doc => {
    this.notes.push({
      id: doc.id,
      title: doc.data().title,
      content: doc.data().content
    });
    });
    });

    Listen to the data changes

    To listen for changes, use onSnapshot() methods of firebase.firestore.CollectionReference to observe events:
    var unsubscribe;

unsubscribe = noteCollection.onSnapshot(snapshot => {
snapshot.docChanges().forEach(change => {
if (change.type === "added") {
console.log("note was added: ", {
...change.doc.data(),
id: change.doc.id
});
}
if (change.type === "modified") {
console.log("note was updated: ", {
...change.doc.data(),
id: change.doc.id
});
}
if (change.type === "removed") {
console.log("note was removed: ", {
...change.doc.data(),
id: change.doc.id
});
}
});
});

// use the unsubscribe function on onSnapshot() to stop listening to updates
unsubscribe();

Full code

App.vue

<template>
    <div id="app">
        <div style="color: blue;">
            <h1>grokonez</h1>
            <h3>Firestore Note App</h3>
        </div>
        <div class="row">
            <div class="col-sm-6">
                <NotesList 
                  @app-addNote="addNote"
                  @app-changeNote="changeNote"
                  :notes="notes"
                  :activeNote="index" />
            </div>
            <div class="col-sm-6">
                <Note
                  @app-saveNote="saveNote"
                  @app-removeNote="removeNote"
                  :note="notes[index]" />
            </div>
        </div>
    </div>
</template>

<script>
import NotesList from "./components/NotesList.vue";
import Note from "./components/Note.vue";
import firebase from "firebase/app";
import "firebase/firestore";

var config = {
  apiKey: "xxx",
  authDomain: "grokonez-vue-firebase.firebaseapp.com",
  databaseURL: "https://grokonez-vue-firebase.firebaseio.com",
  projectId: "grokonez-vue-firebase",
  storageBucket: "grokonez-vue-firebase.appspot.com",
  messagingSenderId: "xxx"
};
firebase.initializeApp(config);

const database = firebase.firestore();
database.settings({
  timestampsInSnapshots: true
});

database.enablePersistence();

const noteCollection = database.collection("notes");
var unsubscribe;

export default {
  name: "app",
  components: {
    NotesList,
    Note
  },
  data: () => ({
    notes: [],
    index: 0
  }),
  methods: {
    addNote() {
      this.notes.push({
        title: "",
        content: ""
      });
      this.index = this.notes.length - 1;
    },
    changeNote(index) {
      this.index = index;
    },
    saveNote() {
      const note = this.notes[this.index];
      if (note.id) {
        this.updateNote(note);
      } else {
        this.createNote(note);
      }
    },
    updateNote(note) {
      noteCollection.doc(note.id).update({
        title: note.title,
        content: note.content
      });
    },
    createNote(note) {
      noteCollection.add(note);
    },
    removeNote() {
      const id = this.notes[this.index].id;
      noteCollection.doc(id).delete();
    }
  },
  created() {
    noteCollection.get().then(snapshot => {
      snapshot.forEach(doc => {
        this.notes.push({
          id: doc.id,
          title: doc.data().title,
          content: doc.data().content
        });
      });
    });

    /* eslint-disable no-console */
    unsubscribe = noteCollection.onSnapshot(snapshot => {
      snapshot.docChanges().forEach(change => {
        if (change.type === "added") {
          const note = { ...change.doc.data(), id: change.doc.id };
          console.log("note was added: ", note);

          this.notes[this.notes.length - 1] = note;
        }
        if (change.type === "modified") {
          const updatedNote = this.notes.find(
            note => note.id === change.doc.id
          );
          updatedNote.title = change.doc.data().title;
          updatedNote.content = change.doc.data().content;

          console.log("note was updated: ", updatedNote);
        }
        if (change.type === "removed") {
          const deletedNote = this.notes.find(
            note => note.id === change.doc.id
          );
          console.log("note was removed: ", deletedNote);

          const index = this.notes.indexOf(deletedNote);
          this.notes.splice(index, 1);
          this.index = this.index === 0 ? 0 : index - 1;
        }
      });
    });
    /* eslint-enable no-console */
  },
  destroyed() {
    unsubscribe();
  }
};
</script>

<style>
#app {
  text-align: center;
  max-width: 700px;
}
</style>

Run

vue-firestore-example

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

Male avatar

loveprogramming

241 bài viết.
67 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
Male avatar
1 0
Tutorial Link: (Link) (Ảnh) Django is a Pythonbased free and opensource web framework that follows the modeltemplateview architectural pattern. A...
loveprogramming viết 5 tháng trước
1 0
Male avatar
1 0
https://loizenai.com/angular11nodejspostgresqlcrudexample/ Angular 11 Node.js PostgreSQL Crud Example (Ảnh) Tutorial: “Angular 11 Node.js Postg...
loveprogramming viết 4 tháng trước
1 0
Male avatar
1 0
Angular Spring Boot jwt Authentication Example Github https://loizenai.com/angularspringbootjwt/ (Ảnh) Tutorial: ” Angular Spring Boot jwt Authe...
loveprogramming viết 4 tháng trước
1 0
Bài viết liên quan
Male avatar
1 0
https://loizenai.com/angular11nodejspostgresqlcrudexample/ Angular 11 Node.js PostgreSQL Crud Example (Ảnh) Tutorial: “Angular 11 Node.js Postg...
loveprogramming viết 4 tháng trước
1 0
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


Male avatar
{{userFollowed ? 'Following' : 'Follow'}}
241 bài viết.
67 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!