Categories
Buefy

Buefy — Date Time Pickers

Buefy is a UI framework that’s based on Bulma.

In this article, we’ll look at how to use Buefy in our Vue app.

Date Time Pickers

We can add a date time picker with Buefy.

For example, we can write:

<template>
  <div>
    <b-field label="Select datetime">
      <b-datetimepicker
        rounded
        placeholder="Click to select..."
        :locale="locale"
        :datepicker="{ showWeekNumber }"
        :timepicker="{ enableSeconds, hourFormat }"
        horizontal-time-picker
      ></b-datetimepicker>
    </b-field>
  </div>
</template>
<script>
export default {
  data() {
    return {
      showWeekNumber: false,
      enableSeconds: false,
      hourFormat: "12h",
      locale: "en-us"
    };
  },

  methods: {}
};
</script>

to add our date time picker with the b-datetimepicker component.

The datepicker and timepicker props let us set the date and time picker options.

We can make the input editable with the editable prop:

<template>
  <div>
    <b-field label="Select datetime">
      <b-datetimepicker
        rounded
        placeholder="Click to select..."
        :locale="locale"
        editable
        :datepicker="{ showWeekNumber }"
        :timepicker="{ enableSeconds, hourFormat }"
        horizontal-time-picker
      ></b-datetimepicker>
    </b-field>
  </div>
</template>
<script>
export default {
  data() {
    return {
      showWeekNumber: false,
      enableSeconds: false,
      hourFormat: "12h",
      locale: "en-us"
    };
  },

  methods: {}
};
</script>

We can change the min-datetime and max-datetime props to restrict the range we can select:

<template>
  <div>
    <b-field label="Select datetime">
      <b-datetimepicker
        placeholder="Click to select..."
        :min-datetime="minDatetime"
        :max-datetime="maxDatetime"
      ></b-datetimepicker>
    </b-field>
  </div>
</template>
<script>
export default {
  data() {
    const min = new Date();
    min.setDate(min.getDate() - 14);
    min.setHours(9);
    min.setMinutes(0);
    min.setSeconds(0);
    const max = new Date();
    max.setDate(max.getDate() + 14);
    max.setHours(18);
    max.setMinutes(0);
    max.setSeconds(0);
    return {
      minDatetime: min,
      maxDatetime: max
    };
  },
  methods: {}
};
</script>

We set the minDatetime and maxDatetime with the Date instances we want to restrict the selected date to be between the given values.

We can populate the left and right slots with the footer with the items we want.

For example, we can write:

<template>
  <div>
    <b-field label="Select datetime">
      <b-datetimepicker v-model="datetime" placeholder="Click to select...">
        <template slot="left">
          <button class="button is-primary" @click="datetime = new Date()">
            <span>Now</span>
          </button>
        </template>
        <template slot="right">
          <button class="button is-danger" @click="datetime = null">
            <span>Clear</span>
          </button>
        </template>
      </b-datetimepicker>
    </b-field>
  </div>
</template>
<script>
export default {
  data() {
    return {
      datetime: undefined
    };
  },
  methods: {}
};
</script>

We added buttons to the left and right of the time picker by populating the slots.

Also, we can add the inline prop to make the date time picker inline:

<template>
  <div>
    <b-field label="Select datetime">
      <b-datetimepicker inline v-model="datetime" placeholder="Click to select..."></b-datetimepicker>
    </b-field>
  </div>
</template>
<script>
export default {
  data() {
    return {
      datetime: undefined
    };
  },
  methods: {}
};
</script>

Conclusion

We can add date-time pickers with Buefy.

Categories
Vue

Using Firebase in a Vue App with Vuexfire — Updating and Removing Documents

The Vuefire library lets us add Firebase database manipulation capabilities right from our Vue app.

In this article, we’ll look at how to use Vuefire and Vuexfire to add support for Cloud Firestore database manipulation into our Vue app.

Updating a Document

We can update a document with the update method.

For instance, we can write:

db.js

import firebase from "firebase/app";
import "firebase/firestore";
export const db = firebase
  .initializeApp({ projectId: "project-id" })
  .firestore();
const { Timestamp, GeoPoint } = firebase.firestore;
export { Timestamp, GeoPoint };

main.js

import Vue from "vue";
import App from "./App.vue";
import { firestorePlugin } from "vuefire";
import { vuexfireMutations, firestoreAction } from "vuexfire";
import Vuex from "vuex";
import { db } from "./db";
Vue.use(Vuex);
Vue.use(firestorePlugin);
Vue.config.productionTip = false;
const store = new Vuex.Store({
  state: {
    books: []
  },
  mutations: {
    ...vuexfireMutations
  },
  actions: {
    bindBooksRef: firestoreAction((context) => {
      return context.bindFirestoreRef(
        "books",
        db.collection("books").orderBy("title", "desc")
      );
    }),
    updateBook: firestoreAction(async ({ state }, { bookId, title }) => {
      await db.collection("books").doc(bookId).update({ title });
      console.log("book updated");
    })
  },
  getters: {
    books: (state) => {
      return state.books;
    }
  }
});
new Vue({
  store,
  render: (h) => h(App)
}).$mount("#app");

App.vue

<template>
  <div>
    <button @click="updateBook">update book</button>
    <div>{{books}}</div>
  </div>
</template>
<script>
import { mapGetters, mapActions } from "vuex";
export default {
  data() {
    return {};
  },
  methods: {
    ...mapActions(["bindBooksRef"]),
    updateBook() {
      this.$store.dispatch("updateBook", {
        bookId: "ei4jIGJjcmS7eSRKUxsw",
        title: "baz"
      });
    }
  },
  computed: {
    ...mapGetters(["books"])
  },
  mounted() {
    this.bindBooksRef();
  }
};
</script>

We have the bindBooksRef action to sync the books Firebase collection with the books state.

Also, we have the updateBook action to update a document within the books collection in the database.

The doc method gets the books document by ID.

update lets us update a document by passing an object with the keys and values we want to update it with.

Removing a Document

We can remove a document with the delete or remove method.

For example, we can write:

main.js

import Vue from "vue";
import App from "./App.vue";
import { firestorePlugin } from "vuefire";
import { vuexfireMutations, firestoreAction } from "vuexfire";
import Vuex from "vuex";
import { db } from "./db";
Vue.use(Vuex);
Vue.use(firestorePlugin);
Vue.config.productionTip = false;
const store = new Vuex.Store({
  state: {
    books: []
  },
  mutations: {
    ...vuexfireMutations
  },
  actions: {
    bindBooksRef: firestoreAction((context) => {
      return context.bindFirestoreRef(
        "books",
        db.collection("books").orderBy("title", "desc")
      );
    }),
    deleteBook: firestoreAction(async ({ state }, bookId) => {
      await db.collection("books").doc(bookId).delete();
      console.log("book updated");
    })
  },
  getters: {
    books: (state) => {
      return state.books;
    }
  }
});
new Vue({
  store,
  render: (h) => h(App)
}).$mount("#app");

App.vue

<template>
  <div>
    <button @click="deleteBook">delete book</button>
    <div>{{books}}</div>
  </div>
</template>
<script>
import { mapGetters, mapActions } from "vuex";
export default {
  data() {
    return {};
  },
  methods: {
    ...mapActions(["bindBooksRef"]),
    deleteBook() {
      this.$store.dispatch("deleteBook", "ei4jIGJjcmS7eSRKUxsw");
    }
  },
  computed: {
    ...mapGetters(["books"])
  },
  mounted() {
    this.bindBooksRef();
  }
};
</script>

We have the deleteBook action which is created by the firestoreAction function.

The callback takes the bookId payload and calls delete to delete the book document with the given ID.

In App.vue , we add the deleteBook method that calls the this.$store.dispatch with the 'deleteBook' action.

The 2nd argument is the payload.

Conclusion

We can create Vuex actions to update and remove documents with a few lines of code with Vuexfire.

Categories
Vue

Using Firebase in a Vue App with Vuexfire — Add Document and Current Timestamp

The Vuefire library lets us add Firebase database manipulation capabilities right from our Vue app.

In this article, we’ll look at how to use Vuefire and Vuexfire to add support for Cloud Firestore database manipulation into our Vue app.

Adding Documents to a Collection

We can add a document to a collection with the add method.

For example, we can write:

db.js

import firebase from "firebase/app";
import "firebase/firestore";
export const db = firebase
  .initializeApp({ projectId: "project-id" })
  .firestore();
const { Timestamp, GeoPoint } = firebase.firestore;
export { Timestamp, GeoPoint };

main.js

import Vue from "vue";
import App from "./App.vue";
import { firestorePlugin } from "vuefire";
import { vuexfireMutations, firestoreAction } from "vuexfire";
import Vuex from "vuex";
import { db } from "./db";
Vue.use(Vuex);
Vue.use(firestorePlugin);
Vue.config.productionTip = false;
const store = new Vuex.Store({
  state: {
    books: []
  },
  mutations: {
    ...vuexfireMutations
  },
  actions: {
    bindBooksRef: firestoreAction((context) => {
      return context.bindFirestoreRef(
        "books",
        db.collection("books").orderBy("title", "desc")
      );
    }),
    addBook: firestoreAction(async ({ state }, book) => {
      await db.collection("books").add(book);
      console.log("book added");
    })
  },
  getters: {
    books: (state) => {
      return state.books;
    }
  }
});
new Vue({
  store,
  render: (h) => h(App)
}).$mount("#app");

App.vue

<template>
  <div>
    <button @click="addBook">add book</button>
    <div>{{books}}</div>
  </div>
</template>
<script>
import { mapGetters, mapActions } from "vuex";
export default {
  data() {
    return {};
  },
  methods: {
    ...mapActions(["bindBooksRef"]),
    addBook() {
      this.$store.dispatch("addBook", { title: "qux" });
    }
  },
  computed: {
    ...mapGetters(["books"])
  },
  mounted() {
    this.bindBooksRef();
  }
};
</script>

We have the bindBookRef action to sync the Firebase books collection with the books state.

Also, we have the addBook action which calls add to add our book object into the books collection.

Then in App.vue , we call this.$store.dispatch method to dispatch the addBook action with the object we want to add.

The button runs the addBook method when we click it.

So when we click it, then the entry is added.

Current Timestamp

We can add the current timestamp at creation or update.

To do that, we can use the firebase.firestore.FieldValue.serverTimestamp method.

For example, we can write:

App.vue

<template>
  <div>
    <button @click="addBook">add book</button>
    <div>{{books}}</div>
  </div>
</template>
<script>
import { mapGetters, mapActions } from "vuex";
import firebase from "firebase/app";

export default {
  data() {
    return {};
  },
  methods: {
    ...mapActions(["bindBooksRef"]),
    addBook() {
      this.$store.dispatch("addBook", {
        title: "qux",
        createdAt: firebase.firestore.FieldValue.serverTimestamp()
      });
    }
  },
  computed: {
    ...mapGetters(["books"])
  },
  mounted() {
    this.bindBooksRef();
  }
};
</script>

We just use it anywhere we want to add a timestamp.

Then we get something like:

{ "createdAt": { "seconds": 1598895154, "nanoseconds": 675000000 }, "title": "qux" }

added to the books collection.

Conclusion

Vuexfire provides us with methods to add documents and current timestamps with our Vue app and Firebase.

Categories
Vue

Adding Authentication to a Vue App with Firebase

Firebase provides us with built-in authentication capabilities.

We can manage users and authenticate them easily with it.

In this article, we’ll look at how to use Firebase’s auth capabilities in a Vue app.

Create Password-Based Accounts

We can create password-based accounts with a few lines of code.

To do this, we can write:

App.vue

<template>
  <div>
    <form @submit.prevent="createUser">
      <input type="text" v-model="email" placeholder="email">
      <input type="password" v-model="password" placeholder="password">
      <input type="submit" value="create user">
    </form>
  </div>
</template>
<script>
import firebase from "firebase/app";
import "firebase/auth";
const firebaseConfig = {
  apiKey: "api-key",
  authDomain: "project-id.firebaseapp.com",
  databaseURL: "https://project-id.firebaseio.com",
  projectId: "project-id",
  storageBucket: "project-id.appspot.com",
  appId: "app-id"
};

export default {
  data() {
    return {
      email: "",
      password: ""
    };
  },
  beforeMount() {
    firebase.initializeApp(firebaseConfig);
  },
  methods: {
    createUser() {
      const { email, password } = this;
      firebase
        .auth()
        .createUserWithEmailAndPassword(email, password)
        .then(() => alert("user creeated"))
        .catch(function(error) {
          console.log(error);
        });
    }
  }
};
</script>

We initialize Firebase with an object to configure it.

The apiKey has the API key.

authDomain is the URL of the Firebase app.

dataBaseURL has the URL to the database.

projectId has the Firebase project ID.

storageBucket is the URL of the storage bucket.

appId is the app’s ID.

In the template, we have the form that accepts an email and password.

And we have the createUser method to create the user.

The createUserWithEmailAndPassword method accepts an email and password string to create the user with the email and password.

The then callback is called when user creation is successful.

catch callback is called when user creation failed.

error has the code and message properties to get the source of the error.

Sign in a User with an Email Address and Password

We can sign in with a username and password with the signInWithEmailAndPassword method.

For example, we can write:

<template>
  <div>
    <form @submit.prevent="signIn">
      <input type="text" v-model="email" placeholder="email">
      <input type="password" v-model="password" placeholder="password">
      <input type="submit" value="sign in">
    </form>
  </div>
</template>
<script>
import firebase from "firebase/app";
import "firebase/auth";
const firebaseConfig = {
  apiKey: "api-key",
  authDomain: "project-id.firebaseapp.com",
  databaseURL: "https://project-id.firebaseio.com",
  projectId: "project-id",
  storageBucket: "project-id.appspot.com",
  appId: "app-id"
};

export default {
  data() {
    return {
      email: "",
      password: ""
    };
  },
  beforeMount() {
    firebase.initializeApp(firebaseConfig);
  },
  methods: {
    signIn() {
      const { email, password } = this;
      firebase
        .auth()
        .signInWithEmailAndPassword(email, password)
        .then(() => alert("sign in successful"))
        .catch(error => {
          console.log(error);
        });
    }
  }
};
</script>

We added the signIn method which is mostly the same as the createUser method.

Sign in with Google

We can add sign in with Google.

First, we go to the Authentication section of our app.

Then we go to the Sign-in method section to add the domain that our app is hosted into the list of authorized domains.

Now we can add the authentication.

For example, we can write:

<template>
  <div>
    <button @click="signIn">sign in with google</button>
  </div>
</template>
<script>
import firebase from "firebase/app";
import "firebase/auth";
const provider = new firebase.auth.GoogleAuthProvider();
const firebaseConfig = {
  apiKey: "api-key",
  authDomain: "project-id.firebaseapp.com",
  databaseURL: "https://project-id.firebaseio.com",
  projectId: "project-id",
  storageBucket: "project-id.appspot.com",
  appId: "app-id"
};

export default {
  data() {
    return {};
  },
  beforeMount() {
    firebase.initializeApp(firebaseConfig);
  },
  methods: {
    signIn() {
      firebase
        .auth()
        .signInWithPopup(provider)
        .then(result => {
          const token = result.credential.accessToken;
          const user = result.user;
          console.log(token, user);
        })
        .catch(error => {
          console.log(error);
          // ...
        });
    }
  }
};
</script>

We create the Google auth provider with the firebase.auth.GoogleAuthProvider constructor.

We added the signIn method which calls the signInWithPopup method with our auth provider object to add the sign-in functionality.

Then the then callback is called to get the user and token .

user has the user data.

token has the auth token.

The catch callback is called when there’s an error and it provides us with info about the error including the email, code, and message.

Conclusion

We can add authentication to our app easily with Firebase into a Vue app.

Categories
Vue Vuetify

Create a Desktop App with Vue, Vuetify, and Electron

Electron is an app framework to let us build desktop apps that are based on web apps.

Vuetify lets us build a web app with Material Design.

We can use the vue-cli-plugin-electron-builder generator to build an Electron app based on Vue.js.

In this article, we’ll look at how to build a simple Electron Vue app with Vuetify and Electron.

Getting Started

We can create our Vue project by running:

npx vue create .

after going into our project folder.

We follow the instructions to create the Vue project.

Then in the project folder, we run:

vue add electron-builder

Once we did that, we add Vuetify to our Vue app by running:

vue add vuetify

Now all the boilerplate code has been added for us.

We then run:

npm run electron:serve

or

yarn electron:serve

to preview our app.

Writing the Code

Now can create our app with Vuetify.

We can create a simple app by adding the following to App.vue :

<template>
  <v-app>
    <v-app-bar app color="primary" dark>
      <div class="d-flex align-center">
        <v-img
          alt="Vuetify Logo"
          class="shrink mr-2"
          contain
          src="https://cdn.vuetifyjs.com/images/logos/vuetify-logo-dark.png"
          transition="scale-transition"
          width="40"
        />

    <span class="mr-2">App</span>
      </div>
    </v-app-bar>

    <v-main>
      <v-form v-model="valid" @submit.prevent="add">
        <v-container>
          <v-row>
            <v-col cols="12" md="6">
              <v-text-field v-model="title" :rules="rule" label="book title" required></v-text-field>
            </v-col>

            <v-col cols="12" md="6">
              <v-text-field v-model="author" :rules="rule" label="book author" required></v-text-field>
            </v-col>
          </v-row>
          <v-row>
            <v-col cols="12">
              <v-btn text type="submit" color="primary">add</v-btn>
            </v-col>
          </v-row>
        </v-container>
      </v-form>

      <v-simple-table>
        <template v-slot:default>
          <thead>
            <tr>
              <th class="text-left">title</th>
              <th class="text-left">author</th>
              <th></th>
            </tr>
          </thead>
          <tbody>
            <tr v-for="(b, i) in books" :key="b.id">
              <td>{{ b.title }}</td>
              <td>{{ b.author }}</td>
              <td>
                <v-btn text color="primary" @click="remove(i)">remove</v-btn>
              </td>
            </tr>
          </tbody>
        </template>
      </v-simple-table>
    </v-main>
  </v-app>
</template>

<script>
import { v4 as uuidv4 } from "uuid";

export default {
  name: "App",
  data: () => ({
    title: "",
    author: "",
    rule: [(v) => !!v || "required"],
    valid: false,
    books: [],
  }),
  methods: {
    add() {
      if (!this.valid) {
        return;
      }
      const { title, author } = this;
      this.books.push({
        id: uuidv4(),
        title,
        author,
      });
    },
    remove(index) {
      this.books.splice(index, 1);
    },
  },
};
</script>

We created a book app with a form and a table.

v-app-bar is the top app bar.

v-main has the main content of the app.

v-form creates the form.

The v-model attribute on the form has the form validation state.

The @submit.prevent directive listens for the submit event to be emitted.

prevent calls preventDefault implicitly.

Inside the form, we have the v-text-field to add the text field.

v-model binds to the model states.

The rules prop has the form validation rules.

The rules are defined with an array of functions with the inputted value as the parameter.

The table is created with the v-simple-table component.

And we populate the default slot with the regular table elements.

In the methods object, we have the add method to let us add entries to this.books .

We check for form data validity with the this.valid property.

A unique ID is generated for each entry with the uuid package.

We need a unique ID for the key prop in the table so the items are rendered correctly.

We install that by running:

npm i uuid

Also, we have the remove method to remove items by its index.

Now we should be able to add and remove items as we wish.

Build Our App

To build our app into an executable file, we run:

yarn electron:build

with Yarn or:

npm run electron:build

with NPM.

Conclusion

We can create our a good looking Vue desktop app with Vuetify, Vue, and the vue-cli-plugin-electron-builder code generator.