Categories
Vuetify

Vuetify — Form Validation

Vuetify is a popular UI framework for Vue apps.

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

Validation with Submit and Clear

We can reset a form with the this.$refs.form.reset() method.

And we can reset form validation with the this.$refs.form.resetValidation() method.

For example, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-form ref="form" v-model="valid" lazy-validation>
          <v-text-field v-model="name" :counter="10" :rules="nameRules" label="Name" required></v-text-field>

          <v-text-field v-model="email" :rules="emailRules" label="E-mail" required></v-text-field>

          <v-select
            v-model="select"
            :items="items"
            :rules="[v => !!v || 'Item is required']"
            label="Item"
            required
          ></v-select>

          <v-checkbox
            v-model="checkbox"
            :rules="[v => !!v || 'You must agree']"
            label="Do you agree?"
            required
          ></v-checkbox>

          <v-btn :disabled="!valid" color="success" class="mr-4" @click="validate">Validate</v-btn>
          <v-btn color="error" class="mr-4" @click="reset">Reset Form</v-btn>
          <v-btn color="warning" @click="resetValidation">Reset Validation</v-btn>
        </v-form>
      </v-col>
    </v-row>
  </v-container>
</template>
<script>
export default {
  name: "HelloWorld",
  data: () => ({
    valid: true,
    name: "",
    nameRules: [
      (v) => !!v || "Name is required",
      (v) => (v && v.length <= 10) || "Name must be less than 10 characters",
    ],
    email: "",
    emailRules: [
      (v) => !!v || "E-mail is required",
      (v) => /.+@.+..+/.test(v) || "E-mail must be valid",
    ],
    select: null,
    items: ["Item 1", "Item 2", "Item 3", "Item 4"],
    checkbox: false,
  }),

  methods: {
    validate() {
      this.$refs.form.validate();
    },
    reset() {
      this.$refs.form.reset();
    },
    resetValidation() {
      this.$refs.form.resetValidation();
    },
  },
};
</script>

We have the rules prop with the rules on each input component.

Also, we have the rules for the name and email.

And the methods let us reset the values and validation with our app.

The validate method lets us validate form fields.

We have the reset and resetValidation methods to reset forms.

Vuelidate

We can incorporate form validation into our Vuetify form by using the Vuelidate library.

For example, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <form>
          <v-text-field
            v-model="name"
            :error-messages="nameErrors"
            :counter="10"
            label="Name"
            required
            [@input](http://twitter.com/input "Twitter profile for @input")="$v.name.$touch()"
            [@blur](http://twitter.com/blur "Twitter profile for @blur")="$v.name.$touch()"
          ></v-text-field>
          <v-text-field
            v-model="email"
            :error-messages="emailErrors"
            label="E-mail"
            required
            [@input](http://twitter.com/input "Twitter profile for @input")="$v.email.$touch()"
            [@blur](http://twitter.com/blur "Twitter profile for @blur")="$v.email.$touch()"
          ></v-text-field>
          <v-select
            v-model="select"
            :items="items"
            :error-messages="selectErrors"
            label="Item"
            required
            [@change](http://twitter.com/change "Twitter profile for @change")="$v.select.$touch()"
            [@blur](http://twitter.com/blur "Twitter profile for @blur")="$v.select.$touch()"
          ></v-select>
          <v-checkbox
            v-model="checkbox"
            :error-messages="checkboxErrors"
            label="Do you agree?"
            required
            [@change](http://twitter.com/change "Twitter profile for @change")="$v.checkbox.$touch()"
            [@blur](http://twitter.com/blur "Twitter profile for @blur")="$v.checkbox.$touch()"
          ></v-checkbox>

<v-btn class="mr-4" [@click](http://twitter.com/click "Twitter profile for @click")="submit">submit</v-btn>
          <v-btn [@click](http://twitter.com/click "Twitter profile for @click")="clear">clear</v-btn>
        </form>
      </v-col>
    </v-row>
  </v-container>
</template>
<script>
import { validationMixin } from "vuelidate";
import { required, maxLength, email } from "vuelidate/lib/validators";

export default {
  name: "HelloWorld",
  mixins: [validationMixin],

validations: {
    name: { required, maxLength: maxLength(10) },
    email: { required, email },
    select: { required },
    checkbox: {
      checked(val) {
        return val;
      },
    },
  },

data: () => ({
    name: "",
    email: "",
    select: null,
    items: ["Item 1", "Item 2", "Item 3", "Item 4"],
    checkbox: false,
  }),

computed: {
    checkboxErrors() {
      const errors = [];
      if (!this.$v.checkbox.$dirty) return errors;
      !this.$v.checkbox.checked && errors.push("You must agree");
      return errors;
    },
    selectErrors() {
      const errors = [];
      if (!this.$v.select.$dirty) return errors;
      !this.$v.select.required && errors.push("Item is required");
      return errors;
    },
    nameErrors() {
      const errors = [];
      if (!this.$v.name.$dirty) return errors;
      !this.$v.name.maxLength &&
        errors.push("Name must be at most 10 characters long");
      !this.$v.name.required && errors.push("Name is required.");
      return errors;
    },
    emailErrors() {
      const errors = [];
      if (!this.$v.email.$dirty) return errors;
      !this.$v.email.email && errors.push("Must be valid e-mail");
      !this.$v.email.required && errors.push("E-mail is required");
      return errors;
    },
  },

  methods: {
    submit() {
      this.$v.$touch();
    },
    clear() {
      this.$v.$reset();
      this.name = "";
      this.email = "";
      this.select = null;
      this.checkbox = false;
    },
  },
};
</script>

to add the form fields.

We incorporate the validationMixin provided by Vuelidate.

And we add the validations object with the name , email , and select keywords, which have the rules.

Also, we have the computed properties with the computed error messages for each field.

We get the fields from the $v object and the keys we have in the validations property.

The return error message can be set with the error-message prop on each field.

In the methods object, we have the reset method to clear the validation.

The items are also reset.

In the submit method, we have the $touch method to trigger the validation.

Conclusion

We can add forms with validation with Vuetify and Vuelidate.

Categories
Vuetify

Vuetify — File Input and Forms

Vuetify is a popular UI framework for Vue apps.

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

Custom Icons for File Input

We can change the icon with the prepend-icon prop.

For example, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-file-input label="File input" filled prepend-icon="mdi-camera"></v-file-input>
      </v-col>
    </v-row>
  </v-container>
</template>
<script>
export default {
  name: "HelloWorld",
};
</script>

We have the prepend-icon prop to add the icon we want to show.

Dense File Input

The dense prop lets us make the file input shorter.

For example, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-file-input label="File input" dense></v-file-input>
      </v-col>
    </v-row>
  </v-container>
</template>
<script>
export default {
  name: "HelloWorld",
};
</script>

We make it smaller with the dense prop.

File Input Selection Slot

We can populate the selection slot to customize the appearance of the input selections.

For example, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-file-input
          v-model="files"
          placeholder="Upload your documents"
          label="File input"
          multiple
          prepend-icon="mdi-paperclip"
        >
          <template v-slot:selection="{ text }">
            <v-chip small label color="primary">{{ text }}</v-chip>
          </template>
        </v-file-input>
      </v-col>
    </v-row>
  </v-container>
</template>
<script>
export default {
  name: "HelloWorld",
  data: () => ({
    files: [],
  }),
};
</script>

We have the v-model to set the state to store the selected files.

In the selection slot, we have the v-chip component to show the selected file name with the chip.

File Input Validation

The rules prop lets us set the rules for validating selected files.

For example, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-file-input
          :rules="rules"
          accept="image/png, image/jpeg, image/bmp"
          placeholder="Pick an image"
          prepend-icon="mdi-camera"
          label="Avatar"
        ></v-file-input>
      </v-col>
    </v-row>
  </v-container>
</template>
<script>
export default {
  name: "HelloWorld",
  data: () => ({
    rules: [
      (value) =>
        !value ||
        value.size < 2000000 ||
        "file is too big",
    ],
  }),
};
</script>

We have the rules array with a function to validate the file.

value has the file we want to validate.

And we return an error message if the selected file isn’t valid.

Forms

We can add forms with Vuetify.

For example, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-container>
          <v-row justify="space-between">
            <v-col cols="12" md="4">
              <v-form ref="form">
                <v-text-field v-model="model" :counter="max" :rules="rules" label="First name"></v-text-field>
              </v-form>
            </v-col>
          </v-row>
        </v-container>
      </v-col>
    </v-row>
  </v-container>
</template>
<script>
export default {
  name: "HelloWorld",
  data: () => ({
    max: 10,
    model: "Foobar",
  }),

  computed: {
    rules() {
      const rules = [];

      if (this.max) {
        const rule = (v) =>
          (v || "").length <= this.max ||
          `A maximum of ${this.max} characters is allowed`;

        rules.push(rule);
      }

       return rules;
    },
  },

  watch: {
    match: "validateField",
    max: "validateField",
    model: "validateField",
  },

  methods: {
    validateField() {
      this.$refs.form.validate();
    },
  },
};
</script>

We have the rules computed property to compute the rules according to the input.

And we validate the form with the validateField method.

We get the ref of the form and call validate on it.

The counter prop sets the max count of the characters.

The rules prop has the validation rules.

Conclusion

We can add file input and forms with Vuetify.

Categories
Vuetify

Vuetify — Combobox and File Input

Vuetify is a popular UI framework for Vue apps.

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

Combobox No Data with Chips

We can add a combobox with chips that have no data in it.

To do that, we can populate the no-data slot to provide the context to the user when searching or creating items.

For example, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-combobox
          v-model="model"
          :items="items"
          :search-input.sync="search"
          hide-selected
          hint="Maximum of 5 tags"
          label="Add some tags"
          multiple
          persistent-hint
          small-chips
        >
          <template v-slot:no-data>
            <v-list-item>
              <v-list-item-content>
                <v-list-item-title>No results matching {{ search }}</v-list-item-title>
              </v-list-item-content>
            </v-list-item>
          </template>
        </v-combobox>
      </v-col>
    </v-row>
  </v-container>
</template>
<script>
export default {
  name: "HelloWorld",
  data: () => ({
    items: ["Programming", "Vue", "Vuetify"],
    model: ["Vuetify"],
    search: null,
  }),

  watch: {
    model(val) {
      if (val.length > 5) {
        this.$nextTick(() => this.model.pop());
      }
    },
  },
};
</script>

We add the no-data and populate it with text to show when there are no matches.

File Inputs

We can add a file input with the v-file-input component.

For instance, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-file-input multiple label="File input"></v-file-input>
      </v-col>
    </v-row>
  </v-container>
</template>
<script>
export default {
  name: "HelloWorld",
};
</script>

to add a file input.

The multiple prop allows for multiple file selection.

File Input Accept Formats

We can set the file formats the file input accept with the accept attribute:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-file-input accept="image/*" label="File input"></v-file-input>
      </v-col>
    </v-row>
  </v-container>
</template>
<script>
export default {
  name: "HelloWorld",
};
</script>

File Input With Chips

We can add file input with chips.

So we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-file-input chips multiple label="File input with chips"></v-file-input>
        <v-file-input small-chips multiple label="File input with small chips"></v-file-input>
      </v-col>
    </v-row>
  </v-container>
</template>
<script>
export default {
  name: "HelloWorld",
};
</script>

We have the chips and small-chips props with the v-file-input .

Size Display

We can add a file size display with the show-size property.

For instance, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-file-input show-size multiple label="File input"></v-file-input>
      </v-col>
    </v-row>
  </v-container>
</template>
<script>
export default {
  name: "HelloWorld",
};
</script>

We have the show-size attribute, so the size will be shown.

Counter

We can add a counter prop with the show-size prop to show the total number of files and the size:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-file-input show-size counter multiple label="File input"></v-file-input>
      </v-col>
    </v-row>
  </v-container>
</template>
<script>
export default {
  name: "HelloWorld",
};
</script>

Conclusion

We can add combobox with various kinds of content.

Also, we can add file inputs with the size and counter.

Categories
Vuetify

Vuetify — Footer

Vuetify is a popular UI framework for Vue apps.

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

Footers

We can add a footer with the v-footer component.

For instance, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-card height="150">
          <v-footer absolute class="font-weight-medium">
            <v-col class="text-center" cols="12">
              {{ new Date().getFullYear() }} —
              <strong>ABC Company</strong>
            </v-col>
          </v-footer>
        </v-card>
      </v-col>
    </v-row>
  </v-container>
</template>

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

We have the v-footer component with the absolute prop to display the footer at the bottom of the v-card .

Padless Footer

We add the padless prop to remove all the default padding from the footer.

For example, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-footer padless class="font-weight-medium">
          <v-col class="text-center" cols="12">
            {{ new Date().getFullYear() }} —
            <strong>ABC Company</strong>
          </v-col>
        </v-footer>
      </v-col>
    </v-row>
  </v-container>
</template>

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

Company Footer

We can create a footer with links:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-footer color="primary lighten-1" padless>
          <v-row justify="center" no-gutters>
            <v-btn
              v-for="link in links"
              :key="link"
              color="white"
              text
              rounded
              class="my-2"
            >{{ link }}</v-btn>
            <v-col class="primary lighten-2 py-4 text-center white--text" cols="12">
              {{ new Date().getFullYear() }} —
              <strong>Vuetify</strong>
            </v-col>
          </v-row>
        </v-footer>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({
    links: ["Home", "About Us", "Contact Us"],
  }),
};
</script>

We render the buttons with the v-btn component.

And we add the v-col to show another bar below it.

Indigo Footer

We can also make a footer with social media buttons.

For example, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-footer dark padless>
          <v-card flat tile class="indigo lighten-1 white--text text-center">
            <v-card-text>
              <v-btn v-for="icon in icons" :key="icon" class="mx-4 white--text" icon>
                <v-icon size="24px">{{ icon }}</v-icon>
              </v-btn>
            </v-card-text>

            <v-card-text
              class="white--text pt-0"
            >Phasellus feugiat arcu sapien, et iaculis ipsum elementum sit amet. Mauris cursus commodo interdum. Praesent ut risus eget metus luctus accumsan id ultrices nunc. Sed at orci sed massa consectetur dignissim a sit amet dui. Duis commodo vitae velit et faucibus. Morbi vehicula lacinia malesuada. Nulla placerat augue vel ipsum ultrices, cursus iaculis dui sollicitudin.</v-card-text>

            <v-divider></v-divider>

            <v-card-text class="white--text">
              {{ new Date().getFullYear() }} —
              <strong>Vuetify</strong>
            </v-card-text>
          </v-card>
        </v-footer>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({
    icons: ["mdi-facebook", "mdi-twitter", "mdi-linkedin", "mdi-instagram"],
  }),
};
</script>

We have the v-card-text components to show the icons and the text below it.

dark makes the background dark.

padless removes the padding.

Conclusion

We can create footers with various styles with Vuetify.

Categories
Vuetify

Vuetify — Expansion Panel Styles

Vuetify is a popular UI framework for Vue apps.

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

Accordion

We can create an expansion panel with accordion style.

For example, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-expansion-panels accordion>
          <v-expansion-panel v-for="(item,i) in 5" :key="i">
            <v-expansion-panel-header>Item</v-expansion-panel-header>
            <v-expansion-panel-content>Lorem ipsum.</v-expansion-panel-content>
          </v-expansion-panel>
        </v-expansion-panels>
      </v-col>
    </v-row>
  </v-container>
</template>

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

We have the accordion prop to make it display like an accordion.

Focusable

The focusable prop lets us make expansion panel headers focusable.

For instance, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <v-expansion-panels focusable>
          <v-expansion-panel v-for="(item,i) in 5" :key="i">
            <v-expansion-panel-header>Item</v-expansion-panel-header>
            <v-expansion-panel-content>Lorem ipsum.</v-expansion-panel-content>
          </v-expansion-panel>
        </v-expansion-panels>
      </v-col>
    </v-row>
  </v-container>
</template>

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

With the focusable prop, the selected expansion panel will have the heading highlighted.

External Control

The v-model prop lets us control which panels are open.

For example, we can write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <div>
          <div class="text-center d-flex pb-4">
            <v-btn [@click](http://twitter.com/click "Twitter profile for @click")="all">all</v-btn>
            <div>{{ panel }}</div>
            <v-btn [@click](http://twitter.com/click "Twitter profile for @click")="none">none</v-btn>
          </div>

<v-expansion-panels v-model="panel" multiple>
            <v-expansion-panel v-for="(item,i) in items" :key="i">
              <v-expansion-panel-header>Header {{ item }}</v-expansion-panel-header>
              <v-expansion-panel-content>Lorem ipsum.</v-expansion-panel-content>
            </v-expansion-panel>
          </v-expansion-panels>
        </div>
      </v-col>
    </v-row>
  </v-container>
</template>

<script>
export default {
  name: "HelloWorld",
  data: () => ({
    panel: [],
    items: 5,
  }),
  methods: {
    all() {
      this.panel = [...Array(this.items).keys()].map((k, i) => i);
    },
    none() {
      this.panel = [];
    },
  },
};
</script>

to create 5 expansion panels.

We have the all method to open all panels.

This works because we have the multiple prop.

The none method closes all the panels by setting this.panel to an empty array.

The v-model on v-expansion-panels controls the open state of the expansion panels.

Custom Icons

We can add custom icons for the top-right icon.

To do that, we write:

<template>
  <v-container>
    <v-row>
      <v-col col="12">
        <div>
          <v-expansion-panels>
            <v-expansion-panel>
              <v-expansion-panel-header>
                Item
                <template v-slot:actions>
                  <v-icon color="primary">$expand</v-icon>
                </template>
              </v-expansion-panel-header>
              <v-expansion-panel-content>Lorem ipsum.</v-expansion-panel-content>
            </v-expansion-panel>

            <v-expansion-panel>
              <v-expansion-panel-header disable-icon-rotate>
                Item
                <template v-slot:actions>
                  <v-icon color="teal">mdi-check</v-icon>
                </template>
              </v-expansion-panel-header>
              <v-expansion-panel-content>Lorem ipsum.</v-expansion-panel-content>
            </v-expansion-panel>

            <v-expansion-panel>
              <v-expansion-panel-header disable-icon-rotate>
                Item
                <template v-slot:actions>
                  <v-icon color="error">mdi-alert-circle</v-icon>
                </template>
              </v-expansion-panel-header>
              <v-expansion-panel-content>Lorem ipsum.</v-expansion-panel-content>
            </v-expansion-panel>
          </v-expansion-panels>
        </div>
      </v-col>
    </v-row>
  </v-container>
</template>

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

We populate the icon within the actions slot.

Conclusion

We can make expansion panels show in an accordion style.

Also, we can add custom icons to expansion panels.