Categories
Vue

Using Firebase in a Vue App Vuefire — Saving Data

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 to add support for Cloud Firestore database manipulation into our Vue app.

Getting Data

We can get data in various ways with Vuefire.

We can call the this.$bind method with a collection.

For example, we can write:

Book.vue

<template>
  <div>{{book.title}}</div>
</template>

<script>
import { db } from "./db";
const books = db.collection("books");

export default {
  props: ["id"],
  data() {
    return {
      book: {}
    };
  },

  watch: {
    id: {
      immediate: true,
      async handler(id) {
        const book = await this.$bind("book", books.doc(id));
        console.log(book);
      }
    }
  }
};
</script>

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

We make the watcher for the id prop async so that we can use await to get the resolved value from this.$bind .

this.$bind returns a promise so we can do that.

book has the resolved value from the Cloud Firestore.

We can do the same for collections.

For example, we can write:

App.vue

<template>
  <div id="app">{{book.title}}</div>
</template>

<script>
import { db } from "./db";
const books = db.collection("books");

export default {
  name: "App",
  data() {
    return {
      title: "title",
      book: {}
    };
  },

  async mounted() {
    const [book] = await this.$bind(
      "books",
      books.where("title", "==", this.title)
    );
    this.book = book;
    console.log(book);
  }
};
</script>

We call the where method to search for an entry with the title instead of getting a document by ID.

The resolved value is destructured so that we can get the entry and display it in the template.

.key / id

The id property of a document isn’t enuerable.

So Object.keys won’t include the key in the array of keys.

But we can access it with the id property directly.

For example, we can write:

<template>
  <div id="app">
    <div v-for="b of books" :key="b.id">{{b.id}} - {{b.title}}</div>
  </div>
</template>

<script>
import { db } from "./db";

export default {
  name: "App",
  data() {
    return {
      books: []
    };
  },
  firestore: {
    books: db.collection("books")
  }
};
</script>

to get the id from books entries in our template.

Geopoints

Geopoints is something we can store with the Cloud Firestore.

For example, we can write:

<template>
  <div id="app">
    <div v-for="b of books" :key="b.id">{{b}}</div>
  </div>
</template>

<script>
import { db, GeoPoint } from "./db";

export default {
  name: "App",
  data() {
    return {
      books: []
    };
  },
  firestore: {
    books: db.collection("cities")
  },
  async mounted() {
    await db.collection("cities").add({
      name: "London",
      location: new GeoPoint(51.3, 0)
    });
  }
};
</script>

to add a GeoPoint instance with the latitude and longitude as its arguments.

Then the object created in the cities collection should have something like:

{ "name": "London", "location": { "latitude": 51.3, "longitude": 0 } }

We have the latitude and longitude properties in the entry.

Timestamps

We can also save timestamps with the Timestamp.fromDate method that comes with Vuefire.

It’s also available with Firestore only.

To use it, we can write:

<template>
  <div id="app">
    <div v-for="b of books" :key="b.id">{{b}}</div>
  </div>
</template>

<script>
import { db, Timestamp } from "./db";

export default {
  name: "App",
  data() {
    return {
      books: []
    };
  },
  firestore: {
    books: db.collection("events")
  },
  async mounted() {
    await db.collection("events").add({
      name: "parade",
      date: Timestamp.fromDate(new Date("2029-07-14"))
    });
  }
};
</script>

We pass in a Date instance into the fromDate method.

Then we get something like:

{ "name": "parade", "date": { "seconds": 1878681600, "nanoseconds": 0 } }

added to the events collection.

seconds is the UNIX timestamp in seconds.

Conclusion

We can save various items to Firebases’ Cloud Firestore in our Vue app with Vuefire.

Categories
Vue

Getting Started with Vue and Firebase with Vuefire

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 to add support for Cloud Firestore database manipulation into our Vue app.

Getting Started

We can get started by going to the Firebase console.

Once we’re logged in, we click Add project to add a project.

Then we click on the project link.

Then we click on the Cloud Firestore link on the left sidebar.

After that, we follow the instructions to create the database and add some entries to it.

Next, we create our Vue app by running npx vue create . in our Vue project folder.

Now we install the Firebase and Vuefire packages by running:

npm install vuefire firebase

or

yarn add vuefire firebase

Vuefire requires Firebase JS SDK 4 or later.

Once we did that, we add a db.js file into the src folder to hold the shared database access code.

In there, we add:

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

We call firebase with the project ID to get access the database.

In main.js , we write:

import Vue from "vue";
import App from "./App.vue";
import { firestorePlugin } from "vuefire";

Vue.use(firestorePlugin);
Vue.config.productionTip = false;

new Vue({
  render: (h) => h(App)
}).$mount("#app");

to add the firestorePlugin plugin from Vuefire so we can use its methods everywhere.

Then in our component file, we can write:

<template>
  <div id="app">
    <div v-for="b of documents" :key="b.id">{{b.title}}</div>
  </div>
</template>

<script>
import { db } from "./db";

export default {
  name: "App",
  data() {
    return {
      documents: []
    };
  },

  firestore: {
    documents: db.collection("books")
  }
};
</script>

to access the books collection with the documents array.

The firestore property maps our Firestore databases to the property we use to access it.

In the template, we just loop through the array with the items we added the Cloud Firestore collection.

If we want to display items by its ID in a component, we’ve to watch for changes to the id and reload the data accordingly.

To do this, we write:

Book.vue

<template>
  <div>{{book.title}}</div>
</template>

<script>
import { db } from "./db";
const books = db.collection("books");

export default {
  props: ["id"],
  data() {
    return {
      book: {}
    };
  },

  watch: {
    id: {
      immediate: true,
      handler(id) {
        this.$bind("book", books.doc(id));
      }
    }
  }
};
</script>

App.vue

<template>
  <div id="app">
    <Book v-for="b of books" :id="b.id" :key="b.id"></Book>
  </div>
</template>

<script>
import { db } from "./db";
import Book from "./Book.vue";

export default {
  name: "App",
  components: {
    Book
  },
  data() {
    return {
      books: []
    };
  },

  firestore: {
    books: db.collection("books")
  }
};
</script>

We add the Book.vue component that watches the id and calls the this.$bind method to get the book entry with the given id .

Then in the template, we display the title property from the entry.

immediate makes the watcher run immediately when the component is loaded.

Then in App.vue , we get the whole books collection in the firestore property and bind the entries to the books state.

In the template of App.vue , we loop through the books entries and render them with the Book component we just created.

Then we should see the entries from books rendered.

Conclusion

Vuefire is a very useful library for accessing our Cloud Firestore database directly from our Vue app.

Categories
Buefy

Buefy — Time and Date 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.

Time Picker Color

We can change the time picker’s color with the type prop.

For example, we can write:

<template>
  <section>
    <b-field label="Select time">
      <b-clockpicker v-model="time" placeholder="Select time" type="is-info" inline></b-clockpicker>
    </b-field>
  </section>
</template>

<script>
export default {
  data() {
    return {
      time: undefined
    };
  }
};
</script>

We have the type prop to set the color of the time picker.

inline makes the time picker display inline.

Other values for type include is-primary , is-success , is-warning , is-danger , is-white , is-black , is-light , and is-dark .

Datepicker

Buefy comes with a datepicker component.

To use it, we can write:

<template>
  <section>
    <b-field label="Select a date">
      <b-datepicker
        v-model="selected"
        show-week-number
        locale="en-ca"
        placeholder="Select date"
        trap-focus
      ></b-datepicker>
    </b-field>
  </section>
</template>

<script>
export default {
  data() {
    return {
      selected: undefined
    };
  }
};
</script>

We have the b-datepicker component with the show-week-number prop to show the week number.

locale changes the locale of the calendar.

placeholder is shown as the placeholder of the input.

trap-focus sets the focus on the input.

Editable Datepicker

We can make the datepicker editable with editable prop.

For example, we can write:

<template>
  <section>
    <b-field label="Select a date">
      <b-datepicker v-model="selected" editable placeholder="Select date" trap-focus></b-datepicker>
    </b-field>
  </section>
</template>

<script>
export default {
  data() {
    return {
      selected: undefined
    };
  }
};
</script>

We can enter the value in MM/DD/YYYY format to set the date value.

Footer

We can add content to the default slot to populate the footer.

For example, we can write:

<template>
  <section>
    <b-field label="Select a date">
      <b-datepicker v-model="date" :first-day-of-week="1" placeholder="Select date">
        <button class="button is-primary" @click="date = new Date()">
          <span>Today</span>
        </button>

        <button class="button is-danger" @click="date = null">
          <span>Clear</span>
        </button>
      </b-datepicker>
    </b-field>
  </section>
</template>

<script>
export default {
  data() {
    return {
      date: new Date()
    };
  }
};
</script>

We put our buttons between the b-datepicker tags and they’ll show below the date picker.

Header

Also, we can add items in the header slot to show in the header.

For example, we can write:

<template>
  <section>
    <b-field label="Select a date">
      <b-datepicker v-model="date" :first-day-of-week="1" placeholder="Select date">
        <template slot="header">
          <button class="button is-primary" @click="date = new Date()">
            <span>Today</span>
          </button>

          <button class="button is-danger" @click="date = null">
            <span>Clear</span>
          </button>
        </template>
      </b-datepicker>
    </b-field>
  </section>
</template>

<script>
export default {
  data() {
    return {
      date: new Date()
    };
  }
};
</script>

We populate the header slot with the buttons to make them show above the datepicker.

Datepicker Trigger

We can add our own date picker trigger by populating the trigger slot:

<template>
  <section>
    <b-datepicker v-model="date" :first-day-of-week="1" placeholder="Select date">
      <template v-slot:trigger>
        <b-button type="is-primary">Pick date</b-button>
      </template>
    </b-datepicker>
  </section>
</template>

<script>
export default {
  data() {
    return {
      date: new Date()
    };
  }
};
</script>

When we click on the button, we’ll see the date picker.

Conclusion

We can add date and time pickers with Buefy to our Vue app.

Categories
Buefy

Buefy — Dropdowns and Autocomplete

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.

Multiple Selection Drop Down

We can set the multiple prop to let us select one or more items.

For example, we can write:

<template>
  <section>
    <b-dropdown v-model="selectedOptions" multiple>
      <button class="button is-primary" type="button" slot="trigger">
        <span>Selected ({{ selectedOptions.length }})</span>
      </button>

      <b-dropdown-item value="option1">
        <span>Option 1</span>
      </b-dropdown-item>

      <b-dropdown-item value="option2">
        <span>Option 2</span>
      </b-dropdown-item>

      <b-dropdown-item value="option3">
        <span>Option 3</span>
      </b-dropdown-item>
    </b-dropdown>
  </section>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      selectedOptions: []
    };
  }
};
</script>

We added the multiple prop to enable multiple selections.

v-model has the selected items.

The value prop has the value of the selected item.

Scrollable Drop Down

We can add the scrollable prop to make the list scrollable.

For example, we can write:

<template>
  <section>
    <b-dropdown :scrollable="isScrollable" :max-height="maxHeight" v-model="currentMenu">
      <button class="button is-primary" type="button" slot="trigger">
        <template>
          <span>{{currentMenu.text}}</span>
        </template>
      </button>

      <b-dropdown-item v-for="(menu, index) in menus" :key="index" :value="menu">
        <div class="media">
          <b-icon class="media-left" :icon="menu.icon"></b-icon>
          <div class="media-content">
            <h3>{{menu.text}}</h3>
          </div>
        </div>
      </b-dropdown-item>
    </b-dropdown>
  </section>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      isScrollable: true,
      maxHeight: 100,
      currentMenu: { text: "People" },
      menus: [{ text: "People" }, { text: "Orders" }, { text: "Settings" }]
    };
  }
};
</script>

to show the dropdown with a max height.

The scrollable prop makes the menu scrollable.

maxHeight sets the max height of the dropdown menu.

Autocomplete

Buefy comes with an autocomplete component.

We can add it by adding the b-autocomplete component:

<template>
  <section>
    <b-field label="favorite fruit">
      <b-autocomplete
        rounded
        v-model="name"
        :data="filteredDataArray"
        placeholder="fruit"
        clearable
        @select="option => selected = option"
      >
        <template slot="empty">No results found</template>
      </b-autocomplete>
    </b-field>
  </section>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      data: ["apple", "orange", "grape"],
      name: ""
    };
  },
  computed: {
    filteredDataArray() {
      return this.data.filter(option => {
        return (
          option
            .toString()
            .toLowerCase()
            .indexOf(this.name.toLowerCase()) >= 0
        );
      });
    }
  }
};
</script>

We have the filteredDataArray computed property to compute the choices that we can select based on what we typed in.

The option parameter has the choice.

The name state has what we typed in since we bound it with v-model .

rounded makes the input rounded.

clearable makes the input clearable.

The @select callback lets us set the choice.

Object Array for Choices

We can use an object array for the choices.

For example, we can write:

<template>
  <section>
    <b-field label="favorite fruit">
      <b-autocomplete
        rounded
        v-model="name"
        :data="filteredDataArray"
        placeholder="fruit"
        clearable
        field="name"
        @select="option => selected = option"
      >
        <template slot="empty">No results found</template>
      </b-autocomplete>
    </b-field>
  </section>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      data: [{ name: "apple" }, { name: "orange" }, { name: "grape" }],
      name: ""
    };
  },
  computed: {
    filteredDataArray() {
      return this.data.filter(option => {
        return (
          option.name
            .toString()
            .toLowerCase()
            .indexOf(this.name.toLowerCase()) >= 0
        );
      });
    }
  }
};
</script>

We set the field prop to the property we want so we can display it as the choice.

Also, we changed the filteredDataArray computed property to use the name property instead of option itself.

Conclusion

We can make a dropdown that let us choose multiple items.

Also, we can add an autocomplete input with Buefy’s b-autocomplete component.

Categories
Buefy

Buefy — Checkbox and Time Picker

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.

Checkbox Sizes

We can change the size of a check with the size prop:

<template>
  <section>
    <div class="field">
      <b-checkbox size="is-small">Small</b-checkbox>
    </div>
    <div class="field">
      <b-checkbox>Default</b-checkbox>
    </div>
    <div class="field">
      <b-checkbox size="is-medium">Medium</b-checkbox>
    </div>
    <div class="field">
      <b-checkbox size="is-large">Large</b-checkbox>
    </div>
  </section>
</template>

Checkbox Types

The type prop lets us change the color of the checkbox.

For example, we can write:

<template>
  <section>
    <div class="field">
      <b-checkbox type="is-info">Checkbox</b-checkbox>
    </div>
  </section>
</template>

to make it blue when it’s checked.

Other values include:

  • is-success
  • is-danger
  • is-warning

Checkbox Button

We can make checkboxes display as buttons.

For example, we can write:

<template>
  <section>
    <b-field>
      <b-checkbox-button v-model="checkboxGroup" native-value="foo" type="is-danger">
        <span>foo</span>
      </b-checkbox-button>

      <b-checkbox-button v-model="checkboxGroup" native-value="bar" type="is-success">
        <span>bar</span>
      </b-checkbox-button>

      <b-checkbox-button v-model="checkboxGroup" native-value="baz">baz</b-checkbox-button>

      <b-checkbox-button v-model="checkboxGroup" native-value="disabled" disabled>Disabled</b-checkbox-button>
    </b-field>
    <p class="content">
      <b>Selection:</b>
      {{ checkboxGroup }}
    </p>
  </section>
</template>

<script>
export default {
  data() {
    return {
      checkboxGroup: []
    };
  }
};
</script>

We have the native-value prop that has the checked value of the checkbox button.

The b-checkbox-button is the checkbox button component.

Time Picker

Buefy comes with a time picker control to let us set the time.

For example, we can use it by writing:

<template>
  <section>
    <b-field label="Select time">
      <b-clockpicker rounded placeholder="Select time" hour-format="12"></b-clockpicker>
    </b-field>
  </section>
</template>

<script>
export default {};
</script>

We set th hour-format to a string with the format.

It can be '12' or '24' .

Non-Read-Only

We can make the input box editable with the editable prop:

<template>
  <section>
    <b-field label="Select time">
      <b-clockpicker editable placeholder="Select time" hour-format="12"></b-clockpicker>
    </b-field>
  </section>
</template>

<script>
export default {};
</script>

Range

The time range that can selected can be limited with the min-time and max-time props:

<template>
  <section>
    <b-field label="Select time">
      <b-clockpicker
        editable
        placeholder="Select time"
        hour-format="12"
        :min-time="minTime"
        :max-time="maxTime"
      ></b-clockpicker>
    </b-field>
  </section>
</template>

<script>
export default {
  data() {
    const min = new Date();
    min.setHours(0);
    min.setMinutes(0);
    const max = new Date();
    max.setHours(3);
    max.setMinutes(0);
    return {
      minTime: min,
      maxTime: max,
      isAmPm: false
    };
  }
};
</script>

We set the time min and max times with the Date instances.

The date part will be ignored.

Footer

Also, we can add a footer to show what we want.

For example, we can write:

<template>
  <section>
    <b-field label="Select time">
      <b-clockpicker v-model="time" placeholder="Select time" hour-format="12">
        <button class="button is-primary" @click="time = new Date()">
          <span>Now</span>
        </button>

        <button class="button is-danger" @click="time = undefined">
          <span>Clear</span>
        </button>
      </b-clockpicker>
    </b-field>
  </section>
</template>

<script>
export default {
  data() {
    return {
      time: undefined
    };
  }
};
</script>

We have the v-model directive to bind to the time state.

And we place the buttons in the default slot to make them show below the clock picker.

We set the time to various values with them.

Conclusion

We can change checkboxes to display what we want.

Also, we can add a time picker with Buefy.