Categories
Ant Design Vue

Ant Design Vue — Custom Validation and v-model

Ant Design Vue or AntD Vue, is a useful UI framework made for Vue.js.

In this article, we’ll look at how to use it in our Vue apps.

Customized Validation

We can add the validation-status prop to show the validation status on the form field:

<template>
  <a-form :label-col="labelCol" :wrapper-col="wrapperCol">
    <a-form-item
      label="Fail"
      validate-status="error"
      help="Should be combination of numbers & alphabets"
    >
      <a-input id="error" placeholder="unavailable choice"/>
    </a-form-item>
  </a-form>
</template>
<script>
export default {
  data() {
    return {
      labelCol: {
        xs: { span: 24 },
        sm: { span: 5 }
      },
      wrapperCol: {
        xs: { span: 24 },
        sm: { span: 12 }
      }
    };
  }
};
</script>

The help prop has the text that is below the form.

labelCol has the size of the labels.

wrapper-col has the wrapper’s columns.

Customized Form Controls

We can customize form controls by nesting our own controls into the a-form-item component:

<template>
  <a-form layout="inline" :form="form" @submit="handleSubmit">
    <a-form-item label="Price">
      <price-input
        v-decorator="[
          'price',
          {
            initialValue: { number: 0, currency: 'pesos' },
            rules: [{ validator: checkPrice }],
          },
        ]"
      />
    </a-form-item>
    <a-form-item>
      <a-button type="primary" html-type="submit">Submit</a-button>
    </a-form-item>
  </a-form>
</template>

<script>
const PriceInput = {
  props: ["value"],
  template: `
    <span>
      <a-input
        type='text'
        :value="number"
        @change="handleNumberChange"
        style="width: 63%; margin-right: 2%;"
      />
      <a-select
        :value="currency"
        style="width: 32%"
        @change="handleCurrencyChange"
      >
        <a-select-option value='pesos'>Pesos</a-select-option>
        <a-select-option value='dollar'>Dollar</a-select-option>
      </a-select>
    </span>
  `,
  data() {
    const value = this.value || {};
    return {
      number: value.number || 0,
      currency: value.currency || "pesos"
    };
  },
  watch: {
    value(val = {}) {
      this.number = val.number || 0;
      this.currency = val.currency || "pesos";
    }
  },
  methods: {
    handleNumberChange(e) {
      const number = parseInt(e.target.value || 0, 10);
      if (isNaN(number)) {
        return;
      }
      this.triggerChange({ number });
    },
    handleCurrencyChange(currency) {
      this.triggerChange({ currency });
    },
    triggerChange(changedValue) {
      this.$emit("change", Object.assign({}, this.$data, changedValue));
    }
  }
};

export default {
  components: {
    PriceInput
  },
  beforeCreate() {
    this.form = this.$form.createForm(this, {
      name: "customized_form_controls"
    });
  },
  methods: {
    handleSubmit(e) {
      e.preventDefault();
      this.form.validateFields((err, values) => {
        if (!err) {
          console.log("Received values of form: ", values);
        }
      });
    },
    checkPrice(rule, value, callback) {
      if (value.number > 0) {
        callback();
        return;
      }
      callback("Price must greater than zero!");
    }
  }
};
</script>

We have the price-input component to display the custom inputs from the component as one form field.

Form Model

We can use form fields with the v-model directive. For example, we can write:

main.js

import Vue from "vue";
import App from "./App.vue";
import Antd from "ant-design-vue";
import "ant-design-vue/dist/antd.css";
import { FormModel } from "ant-design-vue";
Vue.config.productionTip = false;
Vue.use(FormModel);
Vue.use(Antd);

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

App.vue

<template>
  <a-form-model :model="form" :label-col="labelCol" :wrapper-col="wrapperCol">
    <a-form-model-item label="Name">
      <a-input v-model="form.name"/>
    </a-form-model-item>
    <a-form-model-item :wrapper-col="{ span: 14, offset: 4 }">
      <a-button type="primary" @click="onSubmit">Create</a-button>
    </a-form-model-item>
  </a-form-model>
</template>
<script>
export default {
  data() {
    return {
      labelCol: { span: 4 },
      wrapperCol: { span: 14 },
      form: {
        name: ""
      }
    };
  },
  methods: {
    onSubmit() {
      console.log("submit!", this.form);
    }
  }
};
</script>

We register the FormModel plugin to let us use v-model to bind our form fields to reactive properties.

v-model can be used with any other form control component with the FormModel plugin.

Conclusion

Form validation can be customized and we can bind form fields to reactive properties with v-model with Ant Design Vue.

Categories
Ant Design Vue

Ant Design Vue — Checkboxes and Date Pickers

Ant Design Vue or AntD Vue, is a useful UI framework made for Vue.js.

In this article, we’ll look at how to use it in our Vue apps.

Checkbox Groups

We can add checkbox groups with various options.

For example, we can write:

<template>
  <div>
    <a-checkbox-group
      v-model="value"
      name="checkboxgroup"
      :options="plainOptions"
      @change="onChange"
    />
    <br>
    <a-checkbox-group :options="plainOptions" :default-value="['Apple']" @change="onChange"/>
    <br>
    <a-checkbox-group :options="options" :value="['Pear']" @change="onChange"/>
    <br>
    <a-checkbox-group
      :options="optionsWithDisabled"
      disabled
      :default-value="['Apple']"
      @change="onChange"
    >
      <span slot="label" slot-scope="{ value }" style="color: red">{{ value }}</span>
    </a-checkbox-group>
  </div>
</template>
<script>
const plainOptions = ["Apple", "Pear", "Orange"];
const options = [
  { label: "Apple", value: "Apple" },
  { label: "Pear", value: "Pear" },
  { label: "Orange", value: "Orange" }
];
const optionsWithDisabled = [
  { value: "Apple" },
  { label: "Pear", value: "Pear" },
  { label: "Orange", value: "Orange", disabled: false }
];
export default {
  data() {
    return {
      plainOptions,
      options,
      optionsWithDisabled,
      value: []
    };
  },
  methods: {
    onChange(checkedValues) {
      console.log(checkedValues);
      console.log(this.value);
    }
  }
};
</script>

The a-checkbox-group lets us set various options.

v-model binds the checkbox to a reactive property.

options has an array of options. It can be an array of strings.

And it can have array of objects with the label and value properties.

label has the labels and value has the values.

Date Picker

We can add a date picker with the a-date-picker component.

For example, we can write:

<template>
  <div>
    <a-date-picker @change="onChange"/>
    <br>
    <a-month-picker placeholder="Select month" @change="onChange"/>
  </div>
</template>
<script>
export default {
  methods: {
    onChange(date, dateString) {
      console.log(date, dateString);
    }
  }
};
</script>

The a-month-picker component lets us select the month.

The change event is emitted with the date with selected date object.

And the dateString has the string representation of the date.

It also comes with the a-range-picker and a-week-picker to select the date range and the week:

<template>
  <div>
    <a-range-picker @change="onChange"/>
    <br>
    <a-week-picker placeholder="Select week" @change="onChange"/>
  </div>
</template>
<script>
export default {
  methods: {
    onChange(date, dateString) {
      console.log(date, dateString);
    }
  }
};
</script>

Customized Date Rendering

The rendering of the selected date can be changed by populating the dateRender slot:

<template>
  <div>
    <a-date-picker>
      <template slot="dateRender" slot-scope="current, today">
        <div class="ant-calendar-date" :style="getCurrentStyle(current, today)">{{ current.date() }}</div>
      </template>
    </a-date-picker>
    <a-range-picker>
      <template slot="dateRender" slot-scope="current">
        <div class="ant-calendar-date" :style="getCurrentStyle(current)">{{ current.date() }}</div>
      </template>
    </a-range-picker>
    <a-week-picker>
      <template slot="dateRender" slot-scope="current">
        <div class="ant-calendar-date" :style="getCurrentStyle(current)">{{ current.date() }}</div>
      </template>
    </a-week-picker>
  </div>
</template>
<script>
export default {
  methods: {
    getCurrentStyle(current, today) {
      const style = {};
      if (current.date() === 1) {
        style.border = "1px solid #1890ff";
        style.borderRadius = "50%";
      }
      return style;
    }
  }
};
</script>

We set the styles with the getCurrentStyle method.

It takes the current and today parameters to set the border of the date according to whether it’s selected or not.

current.date() has the day of the month. So we render the first of the month with a circle border.

Conclusion

We can add checkbox groups and date pickers to let us add checkboxes and select dates.

Categories
Vue

Add a Virtual Scroll List to our Vue App with vue-virtual-scroll-list

The vue-virtual-scroll-list library lets us add a virtual scroll list to our app.

This way, we can scroll through the items efficiently as they’ll only load when they’re shown on the screen.

In this article, we’ll look at how to use this library to add virtual scroll lists.

Installation

We can install the package by running:

npm install vue-virtual-scroll-list --save

Add the Virtual Scroll List

After installing the package, we can add the virtual scroll list by adding the virtual-list component with a component to display an item.

To do that, we can write:

App.vue

<template>
  <div>
    <virtual-list
      style="height: 360px; overflow-y: auto;"
      :data-key="'uid'"
      :data-sources="items"
      :data-component="itemComponent"
    />
  </div>
</template>

<script>
import Item from "./Item";
import VirtualList from "vue-virtual-scroll-list";

export default {
  name: "root",
  data() {
    return {
      itemComponent: Item,
      items: [{ uid: 1, text: "abc" }, { uid: 2, text: "xyz" }]
    };
  },
  components: { "virtual-list": VirtualList }
};
</script>

Item.vue

<template>
  <div>{{ index }} - {{ source.text }}</div>
</template>

<script>
export default {
  name: "item-component",
  props: {
    index: {
      type: Number
    },
    source: {
      type: Object,
      default() {
        return {};
      }
    }
  }
};
</script>

In App.vue , we have the virtual-list component.

We set the list height and set the overflow-y to auto so that we can make the list scrollable.

data-key is the property name for the ID.

data-source has the source of the data.

data-component is the component for displaying the rows.

Props

It has many other options we can change via props.

The keeps prop is how many items we’re expecting the list to keep rending in the real DOM.

extra-props lets us pass in data not in the data-sources .

estimate-size is th estimate size of the item to adjust the scrollbar.

start sets scroll position of start index.

offset sets the scroll position stay offset.

page-mode sets whether to use the document object to scroll through the list.

item-tag is the tag name to render the rows as.

item-class is the class name to add to the rows.

item-style has the styles of the rows.

header-tag has the tag of the header.

header-class has the tag of the header.

header-style has the style of the header.

We also replace the header with footer to set the styles for the footer.

Events

The virtual-list component also emits a few events.

The scroll event is emitted when scrolling.

totop event is emitted when scrolling to the top or left. The event and range parameters are emitted.

tobottom event is emitted when scrolling to the bottom or right.

resized is emitted when items are resized. The id and size payloads are emitted.

Conclusion

The vue-virtual-scroll-list is useful for adding displaying a virtual scroll list.

Categories
Vue

Add Animation Effects Easily to our Vue App with the vue2-animate Library

Vue comes with animation and transition capabilities.

However, we have to add all the styles for the transitions ourselves.

The vue2-animate library makes this easier for us.

In this article, we’ll look at how to use the library to display animations.

Installation

We can install it by running:

npm i vue2-animate

Then we can import the CSS for the styles by writing:

import Vue from "vue";
import App from "./App.vue";
import "vue2-animate/dist/vue2-animate.min.css";
Vue.config.productionTip = false;

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

Adding the Animations

The vue2-animate library just provide the styles for some animation effects.

We just use it with the transition and transition-group components as we usually would.

For example, we write:

<template>
  <div>
    <button @click="add">add</button>
    <transition-group name="fadeLeft" tag="ul">
      <li v-for="item in items" :key="item.id">{{ item.text }}</li>
    </transition-group>
  </div>
</template>

<script>
import { v4 as uuidv4 } from "uuid";
export default {
  data() {
    return {
      items: Array(5)
        .fill()
        .map(() => ({
          id: uuidv4(),
          text: Math.random()
        }))
    };
  },
  methods: {
    add() {
      this.items.push({
        id: uuidv4(),
        text: Math.random()
      });
    }
  }
};
</script>

to apply the fadeLeft transition effect when we add an item.

Also, we can set the enter-active-class , name , and leave-active-class to the values that are supposed by the library:

<template>
  <div>
    <button @click="toggle = !toggle">toggle</button>
    <transition name="bounce" enter-active-class="bounceInLeft" leave-active-class="bounceOutRight">
      <p v-if="toggle">hello</p>
    </transition>
  </div>
</template>

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

This lets us control the animation for finely.

We can also set the animation duration with the style attribute:

<template>
  <div>
    <button @click="toggle = !toggle">toggle</button>
    <transition name="fade">
      <p v-if="toggle" style="animation-duration: 0.3s">hello</p>
    </transition>
  </div>
</template>

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

Use with Vue Router

We can add animation to route transitions with the bundled styles.

For example, we can write:

main.js

import Vue from "vue";
import App from "./App.vue";
import "vue2-animate/dist/vue2-animate.min.css";
import VueRouter from "vue-router";
Vue.use(VueRouter);
Vue.config.productionTip = false;
const Foo = { template: "<div>foo</div>" };
const Bar = { template: "<div>bar</div>" };

const routes = [
  { path: "/foo", component: Foo },
  { path: "/bar", component: Bar }
];

const router = new VueRouter({
  routes
});

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

App.vue

<template>
  <div>
    <router-link to="foo">foo</router-link>
    <router-link to="bar">bar</router-link>
    <transition
      enter-active-class="animated slideInRight"
      leave-active-class="animated slideOutLeft"
    >
      <router-view appear :key="$router.path"></router-view>
    </transition>
  </div>
</template>

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

We add the routes with the routes array.

And we add the router to our Vue instance.

Then in App.vue , we put the router-view in the transition component.

The appear prop is required for the animation.

The key is also needed to identify when the transition should be shown.

We also have the enter-active-class and leave-active-class for the transition added to add the effects.

Conclusion

The vue2-animate library has the styles for Vue transitions so we don’t have to write our own styles.

Categories
Ant Design Vue

Ant Design Vue — Customize Autocomplete and Cascade Dropdown

Ant Design Vue or AntD Vue, is a useful UI framework made for Vue.js.

In this article, we’ll look at how to use it in our Vue apps.

Customize Autocomplete

We can customize our autocomplete input with various props.

The filter-options prop lets us change how choices are filtered:

<template>
  <a-auto-complete
    :data-source="dataSource"
    style="width: 200px"
    placeholder="input here"
    :filter-option="filterOption"
  />
</template>
<script>
export default {
  data() {
    return {
      dataSource: ["apple", "orange", "grape"]
    };
  },
  methods: {
    filterOption(input, option) {
      return (
        option.componentOptions.children[0].text
          .toUpperCase()
          .indexOf(input.toUpperCase()) >= 0
      );
    }
  }
};
</script>

We get the text input value which is the input parameter with the option.componentOptions.children[0].text property.

Then we compare both of them as uppercase.

If we return true , then it’s displayed in the dropdown.

Cascade Selection Box

We can add a cascade dropdown menu with the a-cascader component.

For example, we can write:

<template>
  <a-cascader :options="options" placeholder="Please select" @change="onChange"/>
</template>
<script>
export default {
  data() {
    return {
      options: [
        {
          value: "fruit",
          label: "Fruit",
          children: [
            {
              value: "apple",
              label: "Apple"
            }
          ]
        },
        {
          value: "drink",
          label: "Drink",
          children: [
            {
              value: "coffee",
              label: "Coffee"
            }
          ]
        }
      ]
    };
  },
  methods: {
    onChange(value) {
      console.log(value);
    }
  }
};
</script>

to add the options with the options reactive property.

We set that as the value of the options prop.

It emits the change event with the selected item.

We can add the change-on-select prop to emit the change event on select:

<template>
  <a-cascader :options="options" placeholder="Please select" change-on-select @change="onChange"/>
</template>
<script>
export default {
  data() {
    return {
      options: [
        {
          value: "fruit",
          label: "Fruit",
          children: [
            {
              value: "apple",
              label: "Apple"
            }
          ]
        },
        {
          value: "drink",
          label: "Drink",
          children: [
            {
              value: "coffee",
              label: "Coffee"
            }
          ]
        }
      ]
    };
  },
  methods: {
    onChange(value) {
      console.log(value);
    }
  }
};
</script>

Also, we can render the selected item in a custom way by populating the displayRender slot:

<template>
  <a-cascader :options="options" placeholder="Please select" change-on-select @change="onChange">
    <template slot="displayRender" slot-scope="{ labels, selectedOptions }">
      <span v-for="(label, index) in labels" :key="selectedOptions[index].value">
        <span v-if="index === labels.length - 1">
          {{ label }} (
          <a @click="e => handleAreaClick(e, label, selectedOptions[index])">
            {{
            selectedOptions[index].code
            }}
          </a>)
        </span>
        <span v-else @click="onChange">{{ label }} /</span>
      </span>
    </template>
  </a-cascader>
</template>
<script>
export default {
  data() {
    return {
      options: [
        {
          value: "fruit",
          label: "Fruit",
          code: 1,
          children: [
            {
              value: "apple",
              label: "Apple",
              code: 2
            }
          ]
        },
        {
          value: "drink",
          label: "Drink",
          code: 3,
          children: [
            {
              value: "coffee",
              label: "Coffee",
              code: 4
            }
          ]
        }
      ]
    };
  },
  methods: {
    onChange(value) {
      console.log(value);
    }
  }
};
</script>

selectedOptions[index] has the selected item.

labels have the label for each value.

Disable Items

We can disable items with the disabled property:

<template>
  <a-cascader :options="options" @change="onChange"/>
</template>
<script>
export default {
  data() {
    return {
      options: [
        {
          value: "fruit",
          label: "Fruit",
          children: [
            {
              value: "apple",
              label: "Apple"
            }
          ]
        },
        {
          value: "drink",
          label: "Drink",
          disabled: true,
          children: [
            {
              value: "coffee",
              label: "Coffee"
            }
          ]
        }
      ]
    };
  },
  methods: {
    onChange(value) {
      console.log(value);
    }
  }
};
</script>

Conclusion

We can customize our autocomplete component with many ways.

Also, Ant Design Vue comes with a cascade selection dropdown.