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.

Categories
Buefy

Buefy — Autocomplete Customization

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.

Header

We can add a custom header to the autocomplete.

To do that, we populate the header slot:

<template>
  <section>
    <b-field label="favorite fruit">
      <b-autocomplete
        rounded
        v-model="name"
        :data="filteredDataArray"
        placeholder="fruit"
        clearable
        ref="autocomplete"
        @select="option => selected = option"
      >
        <template slot="empty">No results found</template>
        <template slot="header">
          <a @click="showAddFruit">
            <span>Add new...</span>
          </a>
        </template>
      </b-autocomplete>
    </b-field>
  </section>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      data: ["apple", "orange", "grape"],
      name: ""
    };
  },
  methods: {
    showAddFruit() {
      this.$buefy.dialog.prompt({
        message: `Fruit`,
        inputAttrs: {
          placeholder: "fruit",
          maxlength: 20,
          value: this.name
        },
        confirmText: "Add",
        onConfirm: value => {
          this.data.push(value);
          this.$refs.autocomplete.setSelected(value);
        }
      });
    }
  },
  computed: {
    filteredDataArray() {
      return this.data.filter(option => {
        return (
          option
            .toString()
            .toLowerCase()
            .indexOf(this.name.toLowerCase()) >= 0
        );
      });
    }
  }
};
</script>

We populate the header slot with a link.

When we click the link, the showAddFruit method is called.

In the method, we show the prompt and set the value to the this.name as the value.

Also, we call this.$refs.autocomplete.setSelected method to set the selected value of the dropdown.

Autocomplete Footer

We can populate the footer slot with the footer.

For example, we can write:

<template>
  <section>
    <b-field label="favorite fruit">
      <b-autocomplete
        rounded
        v-model="name"
        :data="filteredDataArray"
        placeholder="fruit"
        clearable
        ref="autocomplete"
        @select="option => selected = option"
      >
        <template slot="empty">No results found</template>
        <template slot="footer">
          <a @click="showAddFruit">
            <span>Add new...</span>
          </a>
        </template>
      </b-autocomplete>
    </b-field>
  </section>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      data: ["apple", "orange", "grape"],
      name: ""
    };
  },
  methods: {
    showAddFruit() {
      this.$buefy.dialog.prompt({
        message: `Fruit`,
        inputAttrs: {
          placeholder: "fruit",
          maxlength: 20,
          value: this.name
        },
        confirmText: "Add",
        onConfirm: value => {
          this.data.push(value);
          this.$refs.autocomplete.setSelected(value);
        }
      });
    }
  },
  computed: {
    filteredDataArray() {
      return this.data.filter(option => {
        return (
          option
            .toString()
            .toLowerCase()
            .indexOf(this.name.toLowerCase()) >= 0
        );
      });
    }
  }
};
</script>

Now the ‘Add new…’ text is at the bottom of the autocomplete dropdown instead of the top.

Groups

Options can be separated into groups.

For example, we can write:

<template>
  <section>
    <b-field label="favorite fruit">
      <b-autocomplete
        v-model="name"
        group-field="type"
        group-options="items"
        open-on-focus
        :data="filteredDataArray"
        @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: [
        {
          type: "Fruit",
          items: ["Apple", "Banana", "Watermelon"]
        },
        {
          type: "Vegetables",
          items: ["Carrot", "Broccoli", "Cucumber", "Tomato"]
        }
      ],
      name: ""
    };
  },
  computed: {
    filteredDataArray() {
      const newData = [];
      this.data.forEach(element => {
        const items = element.items.filter(item =>
          item.toLowerCase().includes(this.name.toLowerCase())
        );
        if (items.length) {
          newData.push({ type: element.type, items });
        }
      });
      return newData;
    }
  }
};
</script>

to separate our items into groups and search for them.

We have the group-field which is set to the property for separating the groups.

And group-options has the property with the available items.

The filteredDataArray computed property now has to search the items property instead of the whole this.data array.

Conclusion

We can customize our autocomplete dropdowns in many ways with Buefy.