Categories
Vuetify

Vuetify — Expandable Chips and Dialogs

Vuetify is a popular UI framework for Vue apps.

In this article, we’ll look at how to work with the Vuetify framework.

Expandable Chips

A chip can be combined with the v-menu to enable a set of actions for a chip.

For example, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-card max-width="400" class="mx-auto">
          <v-row class="px-6 py-3" align="center">
            <span class="mr-4">To</span>
            <v-menu v-model="menu" bottom right transition="scale-transition" origin="top left">
              <template v-slot:activator="{ on }">
                <v-chip pill v-on="on">
                  <v-avatar left>
                    <v-img src="https://cdn.vuetifyjs.com/images/john.png"></v-img>
                  </v-avatar>John Leider
                </v-chip>
              </template>
              <v-card width="300">
                <v-list dark>
                  <v-list-item>
                    <v-list-item-avatar>
                      <v-img src="https://cdn.vuetifyjs.com/images/john.png"></v-img>
                    </v-list-item-avatar>
                    <v-list-item-content>
                      <v-list-item-title>John Smith</v-list-item-title>
                      <v-list-item-subtitle>john@smith.com</v-list-item-subtitle>
                    </v-list-item-content>
                    <v-list-item-action>
                      <v-btn icon @click="menu = false">
                        <v-icon>mdi-close-circle</v-icon>
                      </v-btn>
                    </v-list-item-action>
                  </v-list-item>
                </v-list>
                <v-list>
                  <v-list-item @click="() => {}">
                    <v-list-item-action>
                      <v-icon>mdi-briefcase</v-icon>
                    </v-list-item-action>
                    <v-list-item-subtitle>john@gmail.com</v-list-item-subtitle>
                  </v-list-item>
                </v-list>
              </v-card>
            </v-menu>
          </v-row>
        </v-card>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({
    menu: false,
  }),
};
</script>

We have the v-menu that wraps outside the v-chip .

The v-chip has some text and an avatar.

Also, the v-list-item has the list that we show when we click on the chip.

The v-on="on" directive lets us toggle the menu since on includes the click listener.

Also, we have a close circle button to set menu to false when we click it.

This way, the menu will close.

Dialogs

The v-dialog lets us display a dialog.

To use it, we write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-row justify="center">
          <v-btn color="primary" dark @click.stop="dialog = true">Open Dialog</v-btn>

          <v-dialog v-model="dialog" max-width="290">
            <v-card>
              <v-card-title class="headline">Title</v-card-title>
              <v-card-text>Lorem ipsum.</v-card-text>
              <v-card-actions>
                <v-spacer></v-spacer>
                <v-btn color="green darken-1" text @click="dialog = false">Cancel</v-btn>
                <v-btn color="green darken-1" text @click="dialog = false">OK</v-btn>
              </v-card-actions>
            </v-card>
          </v-dialog>
        </v-row>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({
    dialog: false,
  }),
};
</script>

to create a dialog with the v-dialog component.

v-model binds to the dialog state to control the opening and closing of the dialog.

We also have the v-btn to set dialog to false when we click on the buttons.

The Open Dialog button sets dialog to true when we click it so the dialog opens when we click it.

Conclusion

We can make a chip show a menu when we click it.

Also, we can create a dialog box with the v-dialog component.

Categories
Vuetify

Vuetify — Chips

Vuetify is a popular UI framework for Vue apps.

In this article, we’ll look at how to work with the Vuetify framework.

Chips

We can use the v-chip component to add a chip.

It conveys a small piece of information.

The close prop lets the chip become interactive.

To add one, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-chip class="ma-2" color="primary">Primary</v-chip>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({}),
};
</script>

We add the v-chip component with the color prop to change the color.

Icon

We can add an icon inside the chip.

For example, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-chip class="ma-2" color="indigo" text-color="white">
          <v-avatar left>
            <v-icon>mdi-account-circle</v-icon>
          </v-avatar>James
        </v-chip>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({}),
};
</script>

We add a v-avatar component with a v-icon inside.

Then we’ll see the icon to the left of the text.

Outlined

The outlined prop will make the chip display with an outline.

For example, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-chip class="ma-2" color="success" outlined>
          <v-icon left>mdi-server-plus</v-icon>Status
        </v-chip>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({}),
};
</script>

Now we’ll see a white background with a border on the chip.

Label

We can make the chip border-radius less round to make a label.

The label prop will adjust the border-radius to be less round:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-chip class="ma-2" color="pink" label text-color="white">
          <v-icon left>mdi-label</v-icon>Tags
        </v-chip>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({}),
};
</script>

Sizes

We can have various sizes with the v-chip component.

For example, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-chip class="ma-2" x-small>x-small</v-chip>
        <v-chip class="ma-2" small>small</v-chip>
        <v-chip class="ma-2">Default</v-chip>
        <v-chip class="ma-2" large>large</v-chip>
        <v-chip class="ma-2" x-large>x-large</v-chip>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({}),
};
</script>

We have the x-small , small , large and x-large props to change the chip size.

Filter

The filter prop lets us show an additional icon if it’s active.

For example, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-chip class="ma-2" :input-value="active" filter filter-icon="mdi-minus">chip</v-chip>
        <v-switch v-model="active" label="Active"></v-switch>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({
    active: false,
  }),
};
</script>

We have the v-chip component with the input-value prop.

When it’s true , then we show an extra icon with the filter prop.

filter-icon lets us set the icon to show with active is true .

Conclusion

We can add chips to show small amounts of information.

The icon can be shown and toggled.

Categories
Vuetify

Vuetify — Chips and Inputs

Vuetify is a popular UI framework for Vue apps.

In this article, we’ll look at how to work with the Vuetify framework.

Closable Chips

We can create a closable chop with the v-model directive.

For example, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-chip
          v-if="chip"
          class="ma-2"
          close
          color="green"
          outlined
          @click:close="chip = false"
        >Success</v-chip>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({
    chip: true,
  }),
};
</script>

We listen to the click:close event with to set chip to false .

The v-if directive controls whether the chip is displayed.

Action Chips

We can have chips that do something when we click it.

For instance, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-chip @click="blinds">
          <v-icon left>mdi-blinds</v-icon>Close blinds
        </v-chip>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({}),
  methods: {
    blinds() {
      alert("Toggling Blinds");
    },
  },
};
</script>

We listen to the click event by setting the click handler top the blinds method.

Then we run the method when we click it.

Chips In Selects

We can use chips in a select element to display selected data.

For instance, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-combobox
          v-model="chips"
          :items="items"
          chips
          clearable
          label="Your favorite hobbies"
          multiple
          solo
        >
          <template v-slot:selection="{ attrs, item, select, selected }">
            <v-chip
              v-bind="attrs"
              :input-value="selected"
              close
              @click="select"
              @click:close="remove(item)"
            >{{ item }}</v-chip>
          </template>
        </v-combobox>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({
    chips: ["eat", "drink", "sleep"],
    items: ["walk", "read"],
  }),
  methods: {
    remove(item) {
      this.chips.splice(this.chips.indexOf(item), 1);
      this.chips = [...this.chips];
    },
  },
};
</script>

We listen to the click event to run the select method provided by the selection slot of the v-combobox component.

v-combobox is the dropdown component.

Also, we listen to the click:close event to remove an item when we click on the ‘x’ on the chip.

Custom Lists

We can use chips in a custom list.

To do that, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-card class="mx-auto" max-width="500">
          <v-container class="py-0">
            <v-row align="center" justify="start">
              <v-col v-for="(selection, i) in selections" :key="selection.text" class="shrink">
                <v-chip :disabled="loading" close @click:close="selected.splice(i, 1)">
                  <v-icon left v-text="selection.icon"></v-icon>
                  {{ selection.text }}
                </v-chip>
              </v-col>

              <v-col v-if="!allSelected" cols="12">
                <v-text-field
                  ref="search"
                  v-model="search"
                  full-width
                  hide-details
                  label="Search"
                  single-line
                ></v-text-field>
              </v-col>
            </v-row>
          </v-container>

          <v-divider v-if="!allSelected"></v-divider>

          <v-list>
            <template v-for="item in categories">
              <v-list-item
                v-if="!selected.includes(item)"
                :key="item.text"
                :disabled="loading"
                @click="selected.push(item)"
              >
                <v-list-item-avatar>
                  <v-icon :disabled="loading" v-text="item.icon"></v-icon>
                </v-list-item-avatar>
                <v-list-item-title v-text="item.text"></v-list-item-title>
              </v-list-item>
            </template>
          </v-list>

          <v-divider></v-divider>

          <v-card-actions>
            <v-spacer></v-spacer>
            <v-btn
              :disabled="!selected.length"
              :loading="loading"
              color="purple"
              text
              @click="next"
            >Next</v-btn>
          </v-card-actions>
        </v-card>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({
    items: [
      {
        text: "nature",
        icon: "mdi-nature",
      },
      {
        text: "wine",
        icon: "mdi-glass-wine",
      },
      {
        text: "calendar",
        icon: "mdi-calendar-range",
      },
    ],
    loading: false,
    search: "",
    selected: [],
  }),

  computed: {
    allSelected() {
      return this.selected.length === this.items.length;
    },
    categories() {
      const search = this.search.toLowerCase();

      if (!search) return this.items;

      return this.items.filter((item) => {
        const text = item.text.toLowerCase();

        return text.indexOf(search) > -1;
      });
    },
    selections() {
      const selections = [];

      for (const selection of this.selected) {
        selections.push(selection);
      }

      return selections;
    },
  },

  watch: {
    selected() {
      this.search = "";
    },
  },

  methods: {
    next() {
      this.loading = true;

      setTimeout(() => {
        this.search = "";
        this.selected = [];
        this.loading = false;
      }, 2000);
    },
  },
};
</script>

We put the v-chip component within the v-col to lay them out when we select items from the v-text-field .

The v-text-field lets us enter some text and the v-list below it will show the items that match what we type.

The categories is a computed property to return the matches.

Conclusion

We can add chips to do different things.

They can also be in dropdowns.

Categories
Vue

Common Vue Problems — Namespaced Vuex Actions, Downloading Binaries, and More

Vue.js makes developing front end apps easy. However, there are still chances that we’ll run into problems.

In this article, we’ll look at some common issues and see how to solve them.

Dispatch Actions Between 2 Namespaced Vuex Actions

Given that we have 2 or more namespace Vuex actions:

game.js

const actions = {
  myAction({dispatch}) {
    ...
    dispatch('notification/triggerSelfDismissingNotifcation', {...})
  }
}

notification.js

const actions = {
  dismiss(context, payload) {
    ...
  }
}

We can dispatch them by adding the namespace before the action name.

For instance, we can write:

dispatch('notification/dismiss', {...}, { root: true })

The 1st argument is the name of the action with the namespace name before it.

The 2nd argument has the payload as usual.

The root property in the 3rd argument is used to access the namespace actions.

The action won’t be found without it.

We also have to remember to set namespaced to true in the Vuex store module.

Get Query Parameters from a URL

We can get query parameters from a URL with the this.$route.query property.

For instance, if we have:

/url?foo=bar

in our URL, then we can get the query parameter with the key foo by writing this.$route.query.foo .

Vue Router Router Link Active Style

We can add styles to the router-link-active class or router-link-exact-active class to add some styles like highlight to an active link.

router-link-active is applied when the target route is matched.

router-link-exct-active is matched when the exact match for a route is found.

To style, we can write something like:

.router-link-active,
.router-link-exact-active {
  background-color: green;
  cursor: pointer;
}

Then we will get a green background for the router-link that’s active.

The class name can be changed.

To do that, we can set the linkActiveClass and linkExactActiveClass properties.

For instance, we can write:

const router = new VueRouter({
  routes,
  linkActiveClass: "active",
  linkExactActiveClass: "exact-active",
})

We change the active class from router-link-active and router-link-exact-active to active and exact-active respectively.

Passing Multiple Parameters to an Action with Vuex

Vuex actions accept a state and pyaload parameter.

So we can pass in an object to the 2nd argument of commit if we need to pass in more than one piece of data.

For instance, we can write:

store.commit('authenticate', {
  token,
  expiration,
});

We have an object with the token and expiration properties in the 2nd argument.

Then to define the authenticate mutation, we can write:

mutations: {
  authenticate(state, { token, expiration }) {
    localStorage.setItem('token', token);
    localStorage.setItem('expiration', expiration);
  }
}

We get the properties from the 2nd parameter, which is the payload parameter.

Then we can do what we want with it, like saving the values in local storage.

Saving Blob Data with Axios

We can save blob data with Axios by creating a new Blob instance.

Then we can call window.URL.ceateObjectURL with it.

For instance, we can write:

axios
  .get(`url/with/pdf`, {
    responseType: 'arraybuffer'
  })
  .then(response => {
     const blob = new Blob([response.data], { type: 'application/pdf' }),
     const url = window.URL.createObjectURL(blob);
     window.open(url);
  })

We have responseType set to 'arraybuffer' to tell Axios that we’re downloading a binary file.

Then we create a Blob instance, with the response.data , which has the binary data, in an array in the 1st argument.

The 2nd argument sets the file mime type to 'application/pdf' .

Then we create the URL to let us download the file with window.URL.createObjectURL .

Finally, we call window.open with then returned URL to download the file to the user’s computer.

Clearing Cache After Each Deploy

We can clear the cache after deploying a Vue app in a few ways.

One way is to use the webpack-assets-manifest to append a random hash name to the file name of static files.

Also, we can upload it to a versioned folder in the CDN.

no-cache headers also work on most browsers excerpt IE, so we can set them to disable caching.

Conclusion

We can clear cache with some packages like webpack-assets-manifest to add a hash to our static files.

Vuex actions can have namespaces so that we can separate them into namespaces.

The payload of a Vuex mutation is its 2nd argument, so we can pass value if we want to pass in one piece of data or an object if we want to pass in more.

Categories
Vue

Common Vue Problems — Bootstrap, Vuex Route Restrictions, and More

Vue.js makes developing front end apps easy. However, there are still chances that we’ll run into problems.

In this article, we’ll look at some common issues and see how to solve them.

Using Bootstrap in our App

If we only want to use the CSS from Bootstrap, then we can add the plain Bootstrap package and load the SASS files from it.

To load SASS files, we can use the sass-loader and node-sass .

To install them, we run:

npm install sass-loader node-sass --save-dev

Then we add the path to load the SASS files in the Webpack config file:

...
sassLoader: {
  includePaths: [
    path.resolve(projectRoot, 'node_modules/bootstrap/scss/'),
  ],
},
...

Now we can import the Bootstrap SASS file in our components:

<style lang="scss">
  @import "bootstrap";
</style>

If we want to use the JavaScript components from Bootstrap, then we should use BootstrapVue instead.

Using SASS Resource Loader

We can add the SASS preprocessor to our vue.config.js file by writing:

module.exports = {
  css: {
    loaderOptions: {
      sass: {
        data: `
          @import "@/scss/_variables.scss";
          @import "@/scss/_mixins.scss";
        `
      }
    }
  }
};

There’s no need for extra dependencies since they’re included with Vue CLI projects.

Call Method in Another Component in Vue

We can’t call another component’s method directly if we aren’t calling a method from a child component in the parent.

Instead, we’ve to pass events around by adding a global Vue instance that we can use to emit and listen to events.

For instance, we can write:

window.Event = new Vue();

to create a Vue instance.

Then we can call $emit on it by writing:

Event.$emit('createImage', item, response)

in one component.

Then in the mounted hook on another component then we can write:

mounted() {
  Event.$on('createImage', (item, response) => {
    // ...
  }
}

We listen to the createImage event and get the arguments that are passed into $emit in the callback parameters.

Adding Web Components in Vue Component

We can add web components in a Vue component directly.

For example, we can write:

<template id="app">
  <web-component>...</web-component>
</template>

Given that web-component is a web component, we can include it directly.

The Limits of v-html

v-html can only be used to render plain HTML code.

We can’t render Vue components with it.

For instance, if we have a component:

Vue.component('component', {
  template: '<span>component</span>',
})

Then we can’t have code like:

new Vue({
  el: '#app',
  data(){
    return {
      comp: '<component></component>'
    }
  }
})

...
<div id="app">
  <span v-html="comp"></span>
</div>

This will not work because component is a component.

All data bindings and Vue components are ignored.

Instead, we have to render plain HTML:

new Vue({
  el: '#app',
  data(){
    return {
      comp: '<p>foo</p>'
    }
  }
})
...
<div id="app">
  <span v-html="comp"></span>
</div>

That will be rendered with v-html since we have a p element.

Mount Multiple Vue Instances on Multiple Elements

If we want to attach multiple Vue instances on multiple elements, we can write:

const vues = document.querySelectorAll(".app");
[...vues].forEach((el, index) => new Vue({el, data: { message: `hello ${index}`}}))

We get all the elements with class app .

Then we use the spread operator on vues so we can use forEach on it,

In the callback, we can create new Vue instances.

Regex in Vue Router Paths

We can use regex in Vue Router paths.

For instance, we can write:

{ path: 'item/:id(d+)', name: 'itemCard', component: ItemCard }

Then we specified that the id parameter must be digits.

Custom Select Component that Binds to v-model

If we want to create custom select component that binds to v-model , then our component has to emit the input event and takes the value prop.

v-model is shorthand for both of those things together.

For instance:

<custom-select v-bind:value="val" v-on:input="val = $event.target.value"></custom-select>

can be rewritten as:

<custom-select v-model="val"></custom-select>

Then we can do anything in custom-event to emit the input event and take the value prop.

Conclusion

v-model is short for emitting the input event and taking the value property.

We can add Bootstrap SASS files with the SASS loader package.

Vue Router routes can have regex to restrict the values of parameters.