DEV Community

Cover image for Vuexfire : How to implement using simple Vue Notes Applicaiton
rajeshpeddalachugari
rajeshpeddalachugari

Posted on

Vuexfire : How to implement using simple Vue Notes Applicaiton

This post is mainly to show the implementation of Vuexfire with the help of simple notes creating application.
I would like to keep it simple and not to add rich features in Notes app as our main focus is on Vuexfire.
So, what is Vuexfire?. lets check it directly from the official document about what it does.

Vuexfire is a small and pragmatic solution to create realtime bindings between a Firebase RTDB or a Firebase Cloudstore and your Vuex store. Making it straightforward to always keep your store state in sync with remotes databases.

Firestore provides two types of databases, Firebase RTDB &Firebase Cloudstore . Vuexfire supports both of them but we will stick to Firebase Cloudstore in this tutorial.

Installing Vuexfire and firebase

In order to get started make sure to install the latest version of Vuexfire as well as firebase:

npm install vuexfire firebase
Enter fullscreen mode Exit fullscreen mode

Create a cloudfire store project

In order to access the cloudfire store we need to create a project in firebase

Select create a project

Alt Text

give a name to the project

Alt Text

create a app in the project by selecting </> button

Alt Text

give a name to the app

Alt Text

firestore configuration code fill be generated, save this somewhere,we will use it later in the application

Alt Text

once everything is created, Select Cloud Firestore in the homepage

Alt Text

Select create database
Alt Text

Select start in test mode as we are not implementing any authentication in our application

Alt Text

Creating Vue App

To have a nice look i have used my all time favorite css framework W3.CSS. Those who are new to css framework i would strongly recommend you to have a look at W3.CSS

Make sure Vuex is added to the project. If not add as follow

npm install vuex --save
Enter fullscreen mode Exit fullscreen mode

lets create a seperate component Notes.Vue for notes item and import it in the Home page, App.vue

Alt Text

Notes.Vue

<template>
  <div class="w3-col w3-container w3-margin-top">
    <div class="w3-card-2 w3-round-large">
      <div v-if="!isEditing">
        <header class="w3-container w3-light-grey mineader headerStyle">
          <h3 class="w3-text-indigo">{{ noteItem.title }}</h3>
        </header>
        <div class="w3-container w3-text-grey minContainer">
          <p>{{ noteItem.content }}</p>
          <hr />
          <div class="w3-rest">
            <button
              @click="removeNote"
              class="w3-right w3-margin-left w3-margin-bottom"
            >
              <i class="fa fa-trash w3-text-indigo" aria-hidden="true"></i>
            </button>
            <button
              @click="editNote"
              class="w3-right  w3-margin-left w3-margin-bottom"
            >
              <i
                class="fa fa-pencil-square-o w3-text-indigo"
                aria-hidden="true"
              ></i>
            </button>
          </div>
        </div>
      </div>

      <div v-if="isEditing">
        <form v-on:submit.prevent="updateNote">
          <div class="w3-container">
            <p>
              <input
                required
                placeholder="Title"
                type="text"
                class="w3-input w3-padding w3-border w3-round-large"
                v-model="noteItem.title"
              />
            </p>
          </div>
          <div class="w3-container w3-text-grey">
            <textarea
              required
              v-model="noteItem.content"
              placeholder="Notes"
              class="w3-input w3-border w3-round-large"
              cols="30"
              rows="3"
            ></textarea>
            <hr />
            <div class="w3-rest">
              <button class="w3-right  w3-margin-left w3-margin-bottom">
                <i class="fa fa-floppy-o w3-text-indigo" aria-hidden="true"></i>
              </button>
            </div>
          </div>
        </form>
      </div>
    </div>
  </div>
</template>
Enter fullscreen mode Exit fullscreen mode
<script>
export default {
  props: ["noteItem"],
  data() {
    return {
      isEditing: false,
    };
  },
  methods: {
    removeNote() {
      this.$emit("remove");
    },
    editNote() {
      this.isEditing = true;
    },
    updateNote() {
      this.isEditing = false;
      this.$emit("update");
    },
  },
};
</script>
Enter fullscreen mode Exit fullscreen mode

for deleting and updating note data we are emitting the event to parent component App.vue

App.vue

<template>
  <div id="app">
    <div class="w3-top">
      <div class="w3-bar w3-indigo" style="letter-spacing:4px;">
        <h3 class="w3-bar-item">Notes</h3>
      </div>
    </div>
    <a
      @click="openModal"
      class="addNew w3-display-bottomright w3-margin-bottom w3-margin-right w3-button w3-xlarge w3-circle w3-pink w3-card-4"
      >+</a
    >
    <div id="noteModal" class="w3-modal">
      <div class="w3-modal-content w3-round-large w3-card-4">
        <div class="w3-container">
          <form v-on:submit.prevent="addNote">
            <p>
              <label>Title</label>
              <input
                required
                v-model="note.title"
                class="w3-input w3-border w3-round-large"
                type="text"
              />
            </p>
            <p>
              <label>Note</label>
              <textarea
                v-model="note.content"
                required
                class="w3-input w3-border w3-round-large"
                id=""
                cols="30"
                rows="3"
              ></textarea>
            </p>
            <div class="w3-bar w3-padding-16">
              <button
                type="submit"
                class="w3-button w3-indigo w3-right w3-margin-right  w3-round"
              >
                Add
              </button>
              <button
                type="button"
                @click="CloseModal"
                class="w3-button w3-indigo w3-right w3-margin-right w3-round"
              >
                Cancel
              </button>
            </div>
          </form>
        </div>
      </div>
    </div>
    <div class="w3-container w3-row-padding notes">
      <div
        is="notes-item"
        v-for="note in Notes"
        v-bind:key="note.id"
        :noteItem="note"
        @delete="deleteNote(note.id)"
        @update="updateNote(note)"
      ></div>
    </div>
  </div>
</template>
Enter fullscreen mode Exit fullscreen mode
<script>
import Notes from "./components/Notes.vue";
import { store } from "./store";
export default {
  name: "App",
  components: {
    "notes-item": Notes,
  },
  data() {
    return {
      note: {
        title: "",
        content: "",
      },
    };
  },
  computed: {
    Notes() {
      return store.state.notes;
    },
  },
  methods: {
    openModal() {
      document.getElementById("noteModal").style.display = "block";
    },
    clearModal() {
      this.note = {
        title: "",
        content: "",
      };
    },
    CloseModal() {
      this.clearModal();
      document.getElementById("noteModal").style.display = "none";
    },
    addNote() {
      store.dispatch("addNote", this.note);
      this.CloseModal();
    },
    updateNote(payload) {
      store.dispatch("updateNote", payload);
    },
    deleteNote(payload) {
      store.dispatch("deleteNote", payload);
    },
  },
  created() {
    store.dispatch("bindNotes");
  },
};
</script>
Enter fullscreen mode Exit fullscreen mode

for creating,updating & deleting notes we will dispatch the actions to the Vuex store in App.vue as follow.

addNote() {
      store.dispatch("addNote", this.note);
      this.CloseModal();
    },
    updateNote(payload) {
      store.dispatch("updateNote", payload);
    },
    deleteNote(payload) {
      store.dispatch("deleteNote", payload);
    },
Enter fullscreen mode Exit fullscreen mode

To bind the notes data with the data already stored in firestore, dispatch an action in Created life cycle hook as follow

created() {
    store.dispatch("bindNotes");
  },
Enter fullscreen mode Exit fullscreen mode

with this basic implementation of app is done. we need to configure firestore in the application to save the data.

Configuring firestore

lets create a db.js file and add the firestore configuration data which we saved during firestore DB creation in the firebase console.

import firebase from 'firebase/app'
import 'firebase/firestore'


// Your web app's Firebase configuration
const firebaseConfig = {
    apiKey: "AIzaSyBTXjgfFvii2nxU05uZFcedh3PNCyy87S8",
    authDomain: "vuex-notes-d9f72.firebaseapp.com",
    databaseURL: "https://vuex-notes-d9f72.firebaseio.com",
    projectId: "vuex-notes-d9f72",
    storageBucket: "vuex-notes-d9f72.appspot.com",
    messagingSenderId: "774907129389",
    appId: "1:774907129389:web:f380d7e4f7c573a96a52a4"
};
// Initialize Firebase
//firebase.initializeApp(firebaseConfig);
export const db = firebase
    .initializeApp(firebaseConfig)
    .firestore()

const { TimeStamp, GeoPoint } = firebase.firestore
export { TimeStamp, GeoPoint }
Enter fullscreen mode Exit fullscreen mode

Adding Vuexfire in the Vuex store

lets import Vuex,db,Vuexfire details into the store file

import Vue from 'vue'
import Vuex from 'vuex'
import { vuexfireMutations, firestoreAction } from 'vuexfire'
import { db } from './db'
Enter fullscreen mode Exit fullscreen mode

lets create a state variable notes as array to store the notes data

export const store = new Vuex.Store({
    state: {
        notes: []
    },
})
Enter fullscreen mode Exit fullscreen mode

Adding Mutations

In order to use Vuexfire, you must add the mutations exported by the package at the root of your store, and only there. Do not add them in any Vuex module:

export const store = new Vuex.Store({
    state: {
        notes: []
    },
    mutations: {
        ...vuexfireMutations,
    },
})
Enter fullscreen mode Exit fullscreen mode

Adding Actions

Before adding the actions in the store lets create the collection notes in the firestore

Alt Text

lets create the following actions

bindNotes :

To bind the date with the firestore data when loading the application

actions: {
        bindNotes: firestoreAction(({ bindFirestoreRef }) => {
            return bindFirestoreRef('notes', db.collection('notes'))
        })
}
Enter fullscreen mode Exit fullscreen mode

first argument in the bindFirestoreRef function refers to the state variable which we created earlier.

addNote

To add the note data to the firestore, here we pass the note data as payload to the firestoreAction

addNote: firestoreAction((context, payload) => {
            return db.collection('notes').add(payload)
        }),
Enter fullscreen mode Exit fullscreen mode

deleteNode

To delete the note from firestore, here we pass the note id which is created by firestore as payload.

deleteNote: firestoreAction((context, payload) => {
            db.collection('notes')
                .doc(payload)
                .delete()
        }),
Enter fullscreen mode Exit fullscreen mode

updateNote

To update the note data, here we pass the note data as payload and
update the existing document in firestore which matches the payload id.

updateNote: firestoreAction((context, payload) => {
            db.collection('notes')
                .doc(payload.id)
                .set(payload)
        }),
Enter fullscreen mode Exit fullscreen mode

Preview

Alt Text

Alt Text

Alt Text

Alt Text


Source code and demo are available in the following links

Github
Demo

Top comments (1)

Collapse
 
brianyamasaki profile image
Brian Yamasaki

I have a suggestion for your App.vue so you don't have to import the store. Use mapGetters and mapActions instead.

import { mapGetters, mapActions } from 'vuex';

export default {
name: 'Notes' ,
methods: {
...mapGetters(['getNotes']),
...mapActions(['bindNotes']),
},
computed: {
Notes() {
return this.getNotes();
}
},
created() {
this.bindNotes();
}
}

Implement getNotes to simply return this.notes in your index.js

Thanks for making this. It helped me figure things out.