Categories
JavaScript Vue

Create a Very Simple Tree View with Vue.js

Recursion can hurt our heads. It’s especially tough when we have to use recursion to display tree structures the way we want.

In this article, we’ll look at how to build a very simple tree view with Vue.js

To get started, we have to create a component to display the tree nodes.

We can do that by create a Tree component in components/Tree.vue:

<template>
  <div>
    <ul>
      <li v-for="i of items" :key="i.name">
        {{i.name}}
        <Tree :items="i.children" v-if="i.children"/>
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  name: "Tree",
  props: {
    items: Array
  }
};
</script>

We have a ul element, which renders the name property of some object in a li.

Then it renders the children property, which is an array with objects with the name and children properties like the current node.

As indicated by the props property, our component takes an items prop, which is an array.

In the li, we pass in the i.chilren into the items prop to render the items one level down.

We check if i.children is defined so that Tree will only render recursively if it’s defined.

Also, we display the i.name value in the current component instance.

The name property is required for recursive components.

Therefore, we should have that.

Then we can create our App.vue component, which renders the root node.

To implement it, we write:

<template>
  <div id="app">
    <Tree :items="items"/>
  </div>
</template>

<script>
import Tree from "./components/Tree";

export default {
  name: "App",
  components: {
    Tree
  },
  data() {
    return {
      items: [
        {
          name: "foo",
          children: [
            { name: "bar", children: [{ name: "baz" }, { name: "qux" }] }
          ]
        },
        {
          name: "lorem",
          children: [{ name: "ipsum" }]
        },
        {
          name: "dolor"
        }
      ]
    };
  }
};
</script>

We have an items array, which is a nested array with objects that have the name and children properties.

The children property has arrays with objects that have the same properties.

This is why we wrote out Tree component the way we have before.

children are rendered recursively, and name is rendered in the current node.

We just pass items straight into the items prop of Tree without changes.

Now we should see a nested list of words displayed on the page in the same order and levels that they’re defined in items.

Categories
JavaScript Vue

Saving Data to Local Storage in a Vue App with Vue-LS

Saving data with the standard JavaScript localStorage object in a Vue app is a pain because it has limited features.

To make our lives easier, we can use a library to make our lives easier.

In this article, we’ll look at how to use the vue-ls library to save data to local storage the Vue way.

Getting Started

We can use the vue-ls library to lets us manipulate the local storage of a browser in an easy way.

To install it, we can run:

npm i vue-ls

Then to use it, we first register the plugin:

import Vue from "vue";
import App from "./App.vue";
import Storage from "vue-ls";

const options = {
  namespace: "vuejs__",
  name: "ls",
  storage: "local"
};

Vue.use(Storage, options);
Vue.config.productionTip = false;

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

We called Vue.use with an options object.

The namespace is the prefix to add the key.

The name is the property name to access the vue-ls methods.

storage is the kind of storage to save the data in. It can be local, storage or memory.

Saving Data

To save data in a component, we can write:

<template>
  <div id="app"></div>
</template>

<script>
export default {
  name: "App",
  mounted() {
    this.$ls.set("foo", "boo", 60 * 60 * 1000);
  }
};
</script>

We called this.$ls.set to save an item with the key vuejs__foo since we prefixed the key withe vuejs__ according to the namespace prop.

The second argument is the key, the 3rd is the expiry time.

The last 2 arguments will be saved as a stringified object.

The key would be vuejs__foo and the value is:

{"value":"boo","expire":1589754925116}

We can check against the expiry timestamp and do what we want with it if it expires.

Adding an expiry time is a feature that isn’t available with local storage.

So, this is a valuable feature.

Watching for Changes

We can also add listen for changes in data with:

<template>
  <div id="app"></div>
</template>

<script>
export default {
  name: "App",
  mounted() {
    const callback = (val, oldVal, uri) => {
      console.log(val, oldVal, uri);
    };

    this.$ls.on("foo", callback);
    this.$ls.set("foo", "boo", 60 * 60 * 1000);
  }
};
</script>

We have the this.$ls.on method. The first argument is the key without the prefix.

The callback it a callback when the local storage item with vuejs__foo is updated.

It has the val parameter with the current value. oldVal has the current value.

uri is the URL that the app is hosted on.

We watch the value with this call.

To unwatch the item, we can write:

this.$ls.off('foo', callback)

Where callback is the same callback we passed into the $on method.

Getting Data

To get a local storage item value by its key, we can write:

this.$ls.get('foo', 10);

Then we get the local storage with the key foo. If the value isn’t found, then 10 is returned.

Finally, to remove a value, we can write:

this.$ls.remove('foo');

to remove the value with the key vuejs__foo assuming the namespace is set to vuejs__.

Conclusion

vue-ls is an easy to use local storage library that lets us do things like adding an expiry timestamp to the saved data and watch for changes to values in addition to what’s already available in browser’s localStorage object.

Categories
JavaScript Vue

Making HTTP Requests in a Vue App with Axios

Most apps need to make HTTP requests to an external server for them to be useful.

Vue.js doesn’t come with its own HTTP client.

However, we can add one ourselves so that we can make requests with it.

One of the best HTTP client libraries is the Axios HTTP client.

To use it, first, we in install it by running:

npm i axios

Then to make requests with it, we can write:

<template>
  <div id="app">{{info}}</div>
</template>

<script>
import axios from "axios";

export default {
  name: "App",
  data() {
    return {
      info: {}
    };
  },
  mounted() {
    axios
      .get("https://api.coindesk.com/v1/bpi/currentprice.json")
      .then(response => (this.info = response.data.bpi));
  }
};
</script>

We make a request to the Conidesk API to get the current prices of Bitcoins.

To make the code shorter, we can write:

<template>
  <div id="app">{{info}}</div>
</template>

<script>
import axios from "axios";

export default {
  name: "App",
  data() {
    return {
      info: {}
    };
  },
  async mounted() {
    const response = await axios.get(
      "https://api.coindesk.com/v1/bpi/currentprice.json"
    );
    this.info = response.data.bpi;
  }
};
</script>

We replaced then with await to make the code shorter.

To deal with errors, we can use try/catch:

<template>
  <div id="app">
    <div v-if="!errored">{{info}}</div>
    <div v-else>error</div>
  </div>
</template>

<script>
import axios from "axios";

export default {
  name: "App",
  data() {
    return {
      info: {},
      errored: false
    };
  },
  async mounted() {
    try {
      const response = await axios.get(
        "https://api.coindesk.com/v1/bpi/currentprice.json"
      );
      this.info = response.data.bpi;
    } catch (error) {
      this.errored = true;
    }
  }
};
</script>

We set this.errored to true in the catch block. This catches the error.

Now we see the price data from the API.

To format the data, we can rewrite our template to display the price.

We can write:

<template>
  <div id="app">
    <div v-if="!errored">
      <p v-for="(value, key) in info" :key="key">{{key}}: {{value.rate}}</p>
    </div>
    <div v-else>error</div>
  </div>
</template>

<script>
import axios from "axios";

export default {
  name: "App",
  data() {
    return {
      info: {},
      errored: false
    };
  },
  async mounted() {
    try {
      const response = await axios.get(
        "https://api.coindesk.com/v1/bpi/currentprice.json"
      );
      this.info = response.data.bpi;
    } catch (error) {
      this.errored = true;
    }
  }
};
</script>

We have the v-for directive to loop through the info object.

The value has the value of a property.

The key has the key, which is the currency code.

We display the code property of the value to display the price.

With Axios, we can make HTTP requests easily within in a Vue.js component.

Categories
JavaScript Vue

Add a Table to a Vue App with Buefy

We can add a table with Buefy with the b-table component.

Basic Table

To populate it with data, we just set the data and columns props.

For example, we write:

<template>
  <div id="app">
    <b-table :data="data" :columns="columns"></b-table>
  </div>
</template>

<script>
export default {
  data() {
    return {
      data: [
        { id: 1, firstName: "jane", lastName: "smith" },
        { id: 2, firstName: "joe", lastName: "smith" },
        { id: 3, firstName: "alex", lastName: "green" }
      ],
      columns: [
        {
          field: "id",
          label: "ID",
          width: "40",
          numeric: true
        },
        {
          field: "firstName",
          label: "First Name"
        },
        {
          field: "lastName",
          label: "Last Name"
        }
      ]
    };
  }
};
</script>

This creates a basic table by using the b-table component.

We set the data prop to data, which has an array of objects.

Each property is mapped to the column by specifying the objects in columns.

field is the property name to display in the column.

label is the table heading to display in the column.

width is the width of the column.

numeric indicates whether the column is numeric.

The columns are displayed in the way they’re listed in the columns array.

So ID is first, First Name is second, and Last Name is third.

Custom Table

We can add other styles.

To make columns customizable, we can populate slots for the content.

We can use the b-table-column to do that:

<template>
  <div id="app">
    <b-table :data="data" hoverable>
      <template slot-scope="props">
        <b-table-column field="id" label="ID" width="40" numeric>{{ props.row.id }}</b-table-column>

        <b-table-column field="firstName" label="First Name">{{ props.row.firstName }}</b-table-column>

        <b-table-column field="lastLame" label="Last Name">{{ props.row.lastName }}</b-table-column>
      </template>

      <template slot="empty">
        <section class="section">
          <div class="has-text-centered">
            <p>Nothing here.</p>
          </div>
        </section>
      </template>
    </b-table>
  </div>
</template>

<script>
export default {
  data() {
    return {
      data: [
        { id: 1, firstName: "jane", lastName: "smith" },
        { id: 2, firstName: "joe", lastName: "smith" },
        { id: 3, firstName: "alex", lastName: "green" }
      ],
      columns: [
        {
          field: "id",
          label: "ID",
          width: "40",
          numeric: true
        },
        {
          field: "firstName",
          label: "First Name"
        },
        {
          field: "lastName",
          label: "Last Name"
        }
      ]
    };
  }
};
</script>

We filled the default slot with b-table-column components.

The props variable has the table data.

We access the with props.row as we did in the code.

The empty slot lets us show something when there’s no data to show in the table.

We replaced the columns prop with the slots.

We can add the bordered, striped, narrowed, hoverable, loading, focusable, and mobile-cards props to set the table options.

mobile-cards means the rows appear as cards on mobile.

The rest is self-explanatory.

In the example above, we added hoverable to b-table to make the rows highlight when hovered.

With Buefy, we can create a table, with the b-table component.

We can either pass in the columns prop to set the columns, or we can use slots to fill the content.

There are also other customization options.

Categories
JavaScript Vue

Add a Dropdown to a Vue App vue-select

We can add a dropdown easily with the vue-select package.

First, we install the package by running:

npm install vue-select

Then we register the plugin by writing:

import Vue from "vue";
import App from "./App.vue";
import vSelect from "vue-select";
import 'vue-select/dist/vue-select.css';

Vue.component("v-select", vSelect);
Vue.config.productionTip = false;

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

We register the component with:

Vue.component("v-select", vSelect);

and added the required CSS with:

import 'vue-select/dist/vue-select.css';

Then we can create a basic drop down in our component by writing:

<template>
  <div id="app">
    <v-select :options="['apple', 'orange']"></v-select>
  </div>
</template>

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

We passed in an array of strings, which will be the display text and the value.

Also, we can pass in an array of objects:

<template>
  <div id="app">
    <v-select label="text" :options="options"></v-select>
  </div>
</template>

<script>
export default {
  data() {
    return {
      options: [
        { value: "apple", text: "Apple" },
        { value: "orange", text: "Orange" }
      ]
    };
  }
};
</script>

We have the options array, and we set the label prop to choose the property to display to the user.

We set it to text, so we display the value of the text property of each entry to the user.

To get the value selected, we can bind the selected value to a state with the v-model directive:

<template>
  <div id="app">
    <v-select label="text" v-model="selected" :options="options"></v-select>
    <p>{{selected}}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      options: [
        { value: "apple", text: "Apple" },
        { value: "orange", text: "Orange" }
      ],
      selected: ""
    };
  },
};
</script>

Now we’ll get the object selected if we pick one from the list.

To assign the value property’s value to selected, we can pass in a function to the reduce prop:

<template>
  <div id="app">
    <v-select label="text" :reduce="reduce" v-model="selected" :options="options"></v-select>
    <p>{{selected}}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      options: [
        { value: "apple", text: "Apple" },
        { value: "orange", text: "Orange" }
      ],
      selected: ""
    };
  },
  methods: {
    reduce(fruit) {
      return fruit.value;
    }
  }
};
</script>

Now the value of value is set as the value of selected when we pick our choice.

To add a placeholder, we can use the placeholder prop:

<template>
  <div id="app">
    <v-select
      label="text"
      placeholder="choose a fruit"
      :reduce="reduce"
      v-model="selected"
      :options="options"
    ></v-select>
    <p>{{selected}}</p>
  </div>
</template>

<script>
export default {
  data() {
    return {
      options: [
        { value: "apple", text: "Apple" },
        { value: "orange", text: "Orange" }
      ],
      selected: ""
    };
  },
  methods: {
    reduce(fruit) {
      return fruit.value;
    }
  }
};
</script>

Now we’ll see ‘choose a fruit’ before we make a choice.

When we click ‘x’ to clear the selection, we’ll see the placeholder again.

We can use the vue-select package to add a customizable drop-down easily.

It can bind to states with v-model and add a placeholder with the placeholder prop.