Categories
Vue

Getting Started with Vue Form Validation with Vee-Validate

Form validation is a feature that’s not built into Vue.js.

However, we still need this feature very much.

In this article, we’ll look at how to add basic form validation with the Vee-Validate library.

Getting Started

We can get started by install the package.

To install it, we run:

npm install vee-validate --save

Then we can register the ValidationProvider plugin by writing:

import Vue from "vue";
import App from "./App.vue";
import { ValidationProvider } from "vee-validate";

Vue.component("ValidationProvider", ValidationProvider);
Vue.config.productionTip = false;

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

in main.js .

This way, we can use it throughout our app.

We can also import extend to cretye our own validation rules:

import Vue from "vue";
import App from "./App.vue";
import { ValidationProvider, extend } from "vee-validate";

extend("secret", {
  validate: value => value === "secret",
  message: "wrong word"
});

Vue.component("ValidationProvider", ValidationProvider);
Vue.config.productionTip = false;

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

value has the inputted value and we return true if it’s valid and false otherwise.

message is the form validation error message that we’ll display.

Vue.component(“ValidationProvider”, ValidationProvider);

will make the ValidationProvider component available globally.

Basic Usage

We can add the ValidationProvider component int our app by writing:

<template>
  <div>
    <ValidationProvider rules="secret" v-slot="{ errors }">
      <input v-model="email" type="text">
      <span>{{ errors[0] }}</span>
    </ValidationProvider>
  </div>
</template>
<script>
export default {
  data(){
    return {
      email: ''
    }
  }
};
</script>

If we type in ‘secret’, then we’ll see nothing.

Otherwise, we’ll see the error message ‘wrong word’ that we set earlier.

errors has the error messages returned by the validator that we have before.

Component Registration

If we just want to use the ValidationProvider component in one component, we can write:

import { ValidationProvider } from 'vee-validate';

export default {
  components: {
    ValidationProvider
  }
};

to register ValidationProvider in the component.

Rule Arguments

We can add arguments to rules.

For instance, we can write:

extend("minLength", {
  validate(value, args) {
    return value.length >= args.length;
  },
  params: ["length"],
  message: "too short"
});

We have a validate method that has the args parameter.

args can have any property specified by the params property.

Therefore, in this case, we can have the length property ion args .

To use it, we can write:

<template>
  <div>
    <ValidationProvider rules="minLength:5" v-slot="{ errors }">
      <input v-model="value" type="text">
      <span>{{ errors[0] }}</span>
    </ValidationProvider>
  </div>
</template>
<script>
export default {
  data() {
    return {
      value: ""
    };
  }
};
</script>

We have:

minLength:5

to set args.length to 5.

So we’ll get an error message if value ‘s length is less than 5.

Multiple Arguments

We can have multiple arguments in our validate method.

For example, we can write:

extend("length", {
  validate(value, { min, max }) {
    return value.length >= min && value.length <= max;
  },
  params: ["min", "max"],
  message: "too short"
});

Then we can use it by writing:

<template>
  <div>
    <ValidationProvider rules="length:5,10" v-slot="{ errors }">
      <input v-model="value" type="text">
      <span>{{ errors[0] }}</span>
    </ValidationProvider>
  </div>
</template>
<script>
export default {
  data() {
    return {
      value: ""
    };
  }
};
</script>

We restructured args into min and max in the arguments.

Then we pass in the arguments separated by a comma.

Then if we enter something that’s shorter than 5 characters or longer than 10, we’ll see an error message.

Vee-Validate will automatically pass in any number of parameters and put it in args.

But putting the property names in params will let us get the by the key instead of getting them as an array.

So we can write something like:

rules="one_of:1,2,3,4,5,6,7,8"

and args will have [1, 2, 3, 4, 5, 6, 7, 8] if we defined a validate method for the one_of rule.

Messages

We can return the message in the validation function as an alternative to setting them in message .

For instance, we can write:

import Vue from "vue";
import App from "./App.vue";
import { ValidationProvider, extend } from "vee-validate";

extend("positive", value => {
  if (value >= 0) {
    return true;
  }
  return "please enter a positive number";
});
Vue.component("ValidationProvider", ValidationProvider);
Vue.config.productionTip = false;

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

Then we can write:

<template>
  <div>
    <ValidationProvider rules="positive" v-slot="{ errors }">
      <input v-model="value" type="text">
      <span>{{ errors[0] }}</span>
    </ValidationProvider>
  </div>
</template>
<script>
export default {
  data() {
    return {
      value: ""
    };
  }
};
</script>

and we’ll see the ‘please enter a positive number’ displayed if we enter anything other than a positive number.

Conclusion

Vee-Validate is an easy to use package for adding form validation capabilities to a Vue app.

It’s much easier than validating inputs with our own code.

Categories
Vue

Create a Dropdown with Vue-MultiSelect

A dropdown is often something we want to add in Vue apps.

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

In this article, we’ll look at how to create a dropdown with Vue-MultiSelect

Getting Started

We can install Vuetify by running:

npm install vue-multiselect --save

Then we can use it by writing:

import Vue from "vue";
import App from "./App.vue";
import Multiselect from "vue-multiselect";
Vue.component("multiselect", Multiselect);
Vue.config.productionTip = false;

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

in main.js .

We add the following to our index.html file:

<link
  rel="stylesheet"
  href="https://unpkg.com/vue-multiselect@2.1.0/dist/vue-multiselect.min.css"
/>

Then we can use it in App.vue by writing:

<template>
  <div id="app">
    <multiselect v-model="value" :options="options"></multiselect>
  </div>
</template>

<script>
export default {
  data() {
    return {
      value: null,
      options: ["foo", "bar", "baz"]
    };
  }
};
</script>

We registered the multiselect component globally with:

import Multiselect from "vue-multiselect";
Vue.component("multiselect", Multiselect);

Therefore, we can use it anywhere.

The options prop takes an array of options.

v-model binds to the selected value.

Now we should see a dropdown with the options listed in options .

Other props include searchables , which lets us type in something to search for it.

placeholder lets us add a placeholder.

close-on-select lets us choose whether the menu closes when an item is selected.

allow-empty indicates whether we want to let users deselect a value of it’s selected.

deselect-label lets us add a message to display if a user tries to deselect an item.

We can write:

<template>
  <div id="app">
    <multiselect v-model="value" deselect-label="Can't remove this value" :options="options"></multiselect>
  </div>
</template>

<script>
export default {
  data() {
    return {
      value: null,
      options: ["foo", "bar", "baz"]
    };
  }
};
</script>

to add the deselect-label option.

Select with Search

We can let users select an item by typing with the searchable prop.

We can add a custom-label prop to lets us pass in a function return a string to display a custom label.

For example, we can write:

<template>
  <div id="app">
    <multiselect v-model="value" searchable :custom-label="nameWithLang" :options="options"></multiselect>
  </div>
</template>

<script>
export default {
  data() {
    return {
      value: null,
      options: [
        { name: "Vue.js", language: "JavaScript" },
        { name: "React", language: "JavaScript" },
        { name: "Rails", language: "Ruby" }
      ]
    };
  },
  methods: {
    nameWithLang({ name, language }) {
      return `${name} — ${language}`;
    }
  }
};
</script>

We have the searchable prop so users can select by typing.

And we have the custom-label prop set to the nameWithLang as the value.

So that’ll be displayed as the value.

Multiple Select

We can enable multiple selections with the multiple prop.

For example, we can write:

<template>
  <div id="app">
    <multiselect v-model="value" multiple :custom-label="nameWithLang" :options="options"></multiselect>
  </div>
</template>

<script>
export default {
  data() {
    return {
      value: null,
      options: [
        { name: "Vue.js", language: "JavaScript" },
        { name: "React", language: "JavaScript" },
        { name: "Rails", language: "Ruby" }
      ]
    };
  },
  methods: {
    nameWithLang({ name, language }) {
      return `${name} — ${language}`;
    }
  }
};
</script>

Then we can select multiple items from the list.

We can populate the tag slot with our own content for the dropdown items.

For instance, we can write:

<template>
  <div id="app">
    <multiselect v-model="value" multiple :options="options">
      <template slot="selection" slot-scope="{ values, search, isOpen }">
        <span
          v-if="values.length && !isOpen"
        >{{ values.length }} options selected</span>
      </template>
    </multiselect>
  </div>
</template>

<script>
export default {
  data() {
    return {
      value: null,
      options: ["Vue.js", "React", "Rails"]
    };
  }
};
</script>

Then we’ll see the number of options we selected.

values has the selected values.

Asynchronous Select

Dropdown options can be loaded asynchronously.

To do that, we can add the loading prop to indicate when it’s loading.

options-limit lets us set the maximum number of options.

limit lets us limit the number of visible results.

internal-search enables or disables the library’s internal search engine.

hide-selected lets us display the items or not.

For example, we can write:

<template>
  <div id="app">
    <multiselect v-model="value" :limit="2" :loading="loading" :options="options"></multiselect>
  </div>
</template>

<script>
export default {
  data() {
    return {
      value: null,
      options: []
    };
  },
  mounted() {
    this.loading = true;
    setTimeout(() => {
      this.options = ["Vue.js", "React", "Rails"];
      this.loading = false;
    }, 100);
  }
};
</script>

Then we set the loading prop to indicate that the items are being loaded.

Conclusion

We can create a dropdown easily with Vue-Multiselect.

All we have to do is to add the plugin and use the built-in component.

Categories
Vue

Vue-MultiSelect — Tags and Templates

A dropdown is often something we want to add in Vue apps.

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

In this article, we’ll look at how to create a dropdown with Vue-MultiSelect with tags and templates.

Tagging

We can make dropdowns taggable with the taggable prop.

Also, we can add a tag placeholder with the tag-placeholder prop.

The tag-position can change the position of the tag.

For example, we can write:

<template>
  <div id="app">
    <multiselect
      v-model="value"
      taggable
      multiple
      tag-placeholder="Add this as new tag"
      tag-position="bottom"
      :options="options"
      @tag="addTag"
    ></multiselect>
  </div>
</template>

<script> export default { data() { return { value: null, options: ["Vue.js", "React", "Rails"] }; }, methods: { addTag(newTag) { this.options.push(newTag); } } }; </script>


We have the `tag` event handler, which is set to the `addTag` function.

The `newTag` parameter has the new tag.

We see the ‘Add this as new tag’ message if we type in something that’s not a choice already on the list.

After we press enter or click on the message, we can select our new option as well.

### Custom Option Template

We can display the options our way.

For example, we can write:

<template> <div id="app"> <multiselect v-model="value" :options="options"> <template slot="singleLabel" slot-scope="props"> <span>{{ props.option.name }}</span> </template> <template slot="option" slot-scope="props"> <span>{{ props.option.name }} – {{ props.option.lang }}</span> </template> </multiselect> </div> </template>

<script>
export default {
  data() {
    return {
      value: null,
      options: [
        { name: "Vue.js", lang: "JavaScript" },
        { name: "React", lang: "JavaScript" },
        { name: "Rails", lang: "Ruby" }
      ]
    };
  }
};
</script>
```

In the dropdown, we see both the `name` and `lang` values because we populate the `option` slot with both.

However, when we select an option, we only see the `name` value because we populated the `singleLabel` slot with it.

### Option Groups

We can add option groups to our dropdown.

For example, we can write:

```
<template>
  <div id="app">
    <multiselect
      v-model="value"
      :options="options"
      group-values="libs"
      group-label="language"
      track-by="name"
      group-select
      :custom-label="customLabel"
    >
      <template slot="noResult">
        <span>no result found.</span>
      </template>
    </multiselect>
  </div>
</template>

<script> export default { data() { return { value: null, options: [ { language: "Javascript", libs: [ { name: "Vue.js", category: "Front-end" }, { name: "React", category: "Backend" } ] }, { language: "Ruby", libs: [ { name: "Rails", category: "Backend" }, { name: "Sinatra", category: "Backend" } ] }, { language: "Other", libs: [{ name: "Laravel", category: "Backend" }] } ] }; }, methods: { customLabel({ name, category }) { return ${name} – ${category}; } } }; </script>


We can select the whole group with the `group-select` prop.

`group-label` lets us select the field to display as the group label.

`groyup-values` lets us select the fields for the groups.

`options` has all the options.

`custom-label` has the function for displaying a custom label for each entry.

### Custom Configuration

We can add custom configuration for our `multiselect` component.

`max-height` sets its height to whatever we want in pixels.

`max` sets the maximum number of selections.

`allow-empty` doesn’t allow the user to remove the last option if it exists if it’s `false` .

`block-keys` are the keys we want to disable for triggering behavior.

`close` lets us pass in an event handler that runs when the dropdown is closing.

For instance, we can write:

<template> <div id="app"> <multiselect v-model="value" multiple :max="3" :options="options"></multiselect> </div> </template>

<script>
export default {
  data() {
    return {
      value: null,
      options: ["foo", "bar", "baz", "qux", "apple"]
    };
  }
};
</script>
```

Then up to 3 choices can be selected.

Otherwise, we’ll see an error message.

### Events

The `multiselect` component emits various events.

`input` is emitted when the selected value changes.

`select` is emitted when the user selects an option.

`remove` is emitted after removing an option.

`SearchChangew` is emitted when the search query changes.

`tag` is emitted when a user tries to add a tag.

`open` is emitted when the dropdown opens.

`close` is emitted when the dropdown closes.

### Conclusion

Vue-Multiselect is very flexible.

We can change many options like the max height, how many options we can select, and much more.

We can also add tags and change how options are displayed by populating slots.
Categories
Vue

vue-i18n — Directive and HTML

Vue.js is an easy to use web app framework that we can use to develop interactive front end apps.

In this article, we’ll look at how to create multilingual Vue apps with vue-i18n.

Custom Directive Localization

We can use the v-t directive for getting translations.

For instance, we can write:

main.js

import Vue from "vue";
import App from "./App.vue";
import VueI18n from "vue-i18n";

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

const messages = {
  en: {
    hello: "hi"
  },
  fr: {
    hello: "bonjour"
  }
};

const i18n = new VueI18n({
  messages,
  locale: "fr"
});

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

App.vue

<template>
  <div id="app">
    <p v-t="'hello'"></p>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

We just put the v-t directive in our template and we get the translation.

So bonjour is displayed on the screen.

We can also write:

main.js

import Vue from "vue";
import App from "./App.vue";
import VueI18n from "vue-i18n";

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

const messages = {
  en: {
    hello: "hi {name}"
  },
  fr: {
    hello: "bonjour {name}"
  }
};

const i18n = new VueI18n({
  messages,
  locale: "fr"
});

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

App.vue

<template>
  <div id="app">
    <p v-t="{ path: 'hello', args: { name: nickName } }"></p>
  </div>
</template>

<script>
export default {
  name: "App",
  computed: {
    nickName() {
      return "foo";
    }
  }
};
</script>

to interpolate translation.

The path has the path to the message, args has dynamic arguments.

Now we get bonjour foo displayed on the screen.

Use with Transitions

We can use the v-t directive with transitions.

For instance, we can write:

main.js

import Vue from "vue";
import App from "./App.vue";
import VueI18n from "vue-i18n";

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

const messages = {
  en: {
    hello: "hi"
  },
  fr: {
    hello: "bonjour"
  }
};

const i18n = new VueI18n({
  messages,
  locale: "fr"
});

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

App.vue

<template>
  <div id="app">
    <button @click="toggle = !toggle">Toggle</button>
    <transition name="fade">
      <p v-t="'hello'" v-if="toggle"></p>
    </transition>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      toggle: true
    };
  }
};
</script>

<style>
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.5s;
}
.fade-enter,
.fade-leave-to {
  opacity: 0;
}
</style>

Then we get a transition effect thanks to the transition component.

$t vs v-t

$t is more flexible in that it can be used in component code and template.

However, it’s run every time a re-render occurs, which means it’s slower.

v-t has better performance than $t because of caching.

It’s also can be used for pre-translation with vue-i18n-extensions .

However, it isn’t as flexible as $t and it’s complex.

Component Interpolation

To add translations with HTML, we may want to put them ibn between our tags:

main.js

import Vue from "vue";
import App from "./App.vue";
import VueI18n from "vue-i18n";

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

const messages = {
  en: {
    hello: "hi",
    world: "world"
  },
  fr: {
    hello: "bonjour",
    world: "monde"
  }
};

const i18n = new VueI18n({
  messages,
  locale: "fr"
});

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

App.vue

<template>
  <div id="app">
    <p>
      {{ $t('hello') }}
      <b>{{ $t('world') }}</b>
    </p>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

However, this is too inflexible.

We can use the i18n component to avoid this.

For instance, we can write:

main.js

import Vue from "vue";
import App from "./App.vue";
import VueI18n from "vue-i18n";

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

const messages = {
  en: {
    hello: "hi {0}",
    world: "world"
  },
  fr: {
    hello: "bonjour {0}",
    world: "monde"
  }
};

const i18n = new VueI18n({
  messages,
  locale: "fr"
});

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

App.vue

<template>
  <div id="app">
    <i18n path="hello" tag="p" for="world">
      <b>{{ $t('world') }}</b>
    </i18n>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

We have the {0} placeholder to interpolate what we have inside i18n into the tags.

Then we see the 2nd word in bold.

Conclusion

We can put HTML formatted text in the i18n component.

Also, we can use the v-t directive as an alternative to $t .

It’s faster and works with transitions.

Categories
Vue

vue-i18n — Fallback Localization and Local Translations

Vue.js is an easy to use web app framework that we can use to develop interactive front end apps.

In this article, we’ll look at how to create multilingual Vue apps with vue-i18n.

Fallback Localization

We can set a fallbackLocale in case the string isn’t available.

vue-18n has implicit fallback locale.

A more specific locale would fall back to the more general of the same kind.

For example, de-DE would fall back to de .

We can also specify fallback locales explicitly using the fallbackLocale property.

For instances, we can write:

import Vue from "vue";
import App from "./App.vue";
import VueI18n from "vue-i18n";

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

const messages = {
  en: {
    foo: "hello"
  },
  fr: {}
};

const i18n = new VueI18n({
  messages,
  locale: "fr",
  fallbackLocale: "en"
});

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

Then we write:

<template>
  <div id="app">
    <p>{{ $t('foo') }}</p>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

Then we see hello displayed because our fallback locale is en .

fallbackLocale can also be an array, so we can write:

import Vue from "vue";
import App from "./App.vue";
import VueI18n from "vue-i18n";

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

const messages = {
  en: {
    foo: "hello"
  },
  fr: {},
  ja: {}
};

const i18n = new VueI18n({
  messages,
  locale: "fr",
  fallbackLocale: ["fr", "en"]
});

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

It can also be an object with a list of fallback locales.

The key is the locale and the value is its fallback.

For example, we can write:

import Vue from "vue";
import App from "./App.vue";
import VueI18n from "vue-i18n";

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

const messages = {
  en: {
    foo: "hello"
  }
};

const i18n = new VueI18n({
  messages,
  locale: "fr",
  fallbackLocale: {
    "de-CH": ["fr", "it"],
    default: ["en", "da"]
  }
});

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

Then de-CH falls back to fr or it . If those aren’t available, then it falls back to en or da .

Fallback Interpolation

Fallback locales can have their own interpolation.

We’ve to put the interpolation in the key.

For example, we can write:

import Vue from "vue";
import App from "./App.vue";
import VueI18n from "vue-i18n";

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

const messages = {
  en: {
    "hello {name}": "hello {name}"
  }
};

const i18n = new VueI18n({
  messages,
  locale: "fr"
});

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

Then in our template, we write:

<template>
  <div id="app">
    <p>{{ $t('hello {name}', { name: 'james' }) }}</p>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

Component-Based Localization

We can localization that’s only in a component.

For instance, we can write:

main.js

import Vue from "vue";
import App from "./App.vue";
import VueI18n from "vue-i18n";

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

const messages = {
  en: {
    "hello {name}": "hello {name}"
  }
};

const i18n = new VueI18n({
  messages,
  locale: "fr"
});

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

App.vue

<template>
  <div id="app">
    <p>{{ $t('message.welcome') }}</p>
  </div>
</template>

<script>
export default {
  name: "App",
  i18n: {
    messages: {
      en: { message: { welcome: "welcome" } },
      fr: { message: { welcome: "bienvenu" } }
    }
  }
};
</script>

We added an i18n prooperty in our component to provide local translations.

The messages property has the translations.

Then we define the translations as usual.

Since locale is set to 'fr' , we get bienvenu on the screen.

These translations can be saved in another module and imported.

For instance, we can write:

messages.js

export default {
  en: { message: { welcome: "welcome" } },
  fr: { message: { welcome: "bienvenu" } }
};

App.vue

<template>
  <div id="app">
    <p>{{ $t('message.welcome') }}</p>
  </div>
</template>

<script>
import messages from "./messages";
export default {
  name: "App",
  i18n: {
    messages
  }
};
</script>

We can use parent.$t to get translations in a function component.

For instance, we can write:

<div>{{ parent.$t('message.hello') }}</div>

to get the translation in a function component.

Conclusion

We can get translations in various ways.

One way is to get them from fallback locales.

We can define them as objects, arrays, or strings.

Also, we can have translations that are exclusive to some components.