Categories
Ant Design Vue

Ant Design Vue — Form Validation and Layout

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.

Form Validation Rules

We can set the rules with the rules prop:

<template>
  <a-form :form="form">
    <a-form-item
      :label-col="formItemLayout.labelCol"
      :wrapper-col="formItemLayout.wrapperCol"
      label="Name"
    >
      <a-input
        v-decorator="[
          'username',
          { rules: [{ required: true, message: 'Please input your name' }] },
        ]"
        placeholder="Please input your name"
      />
    </a-form-item>
    <a-form-item
      :label-col="formItemLayout.labelCol"
      :wrapper-col="formItemLayout.wrapperCol"
      label="Nickname"
    >
      <a-input
        v-decorator="[
          'nickname',
          { rules: [{ required: checkNick, message: 'Please input your nickname' }] },
        ]"
        placeholder="Please input your nickname"
      />
    </a-form-item>
    <a-form-item :label-col="formTailLayout.labelCol" :wrapper-col="formTailLayout.wrapperCol">
      <a-checkbox :checked="checkNick" @change="handleChange">Nickname is required</a-checkbox>
    </a-form-item>
    <a-form-item :label-col="formTailLayout.labelCol" :wrapper-col="formTailLayout.wrapperCol">
      <a-button type="primary" @click="check">Check</a-button>
    </a-form-item>
  </a-form>
</template>

<script>
const formItemLayout = {
  labelCol: { span: 4 },
  wrapperCol: { span: 8 }
};
const formTailLayout = {
  labelCol: { span: 4 },
  wrapperCol: { span: 8, offset: 4 }
};
export default {
  data() {
    return {
      checkNick: false,
      formItemLayout,
      formTailLayout,
      form: this.$form.createForm(this, { name: "dynamic_rule" })
    };
  },
  methods: {
    check() {
      this.form.validateFields(err => {
        if (!err) {
          console.info("success");
        }
      });
    },
    handleChange(e) {
      this.checkNick = e.target.checked;
      this.$nextTick(() => {
        this.form.validateFields(["nickname"], { force: true });
      });
    }
  }
};
</script>

We set the required property in the object we added to the rules property to the checkNick reactive property so that we can make the nickname required only when the Nickname is required checkbox is checked.

Inline Form

We can set the layout prop to inline to make the form fields display inline:

<template>
  <a-form layout="inline" :form="form" @submit="handleSubmit">
    <a-form-item :validate-status="userNameError() ? 'error' : ''" :help="userNameError() || ''">
      <a-input
        v-decorator="[
          'userName',
          { rules: [{ required: true, message: 'Please input your username!' }] },
        ]"
        placeholder="Username"
      >
        <a-icon slot="prefix" type="user" style="color:rgba(0,0,0,.25)"/>
      </a-input>
    </a-form-item>
    <a-form-item :validate-status="passwordError() ? 'error' : ''" :help="passwordError() || ''">
      <a-input
        v-decorator="[
          'password',
          { rules: [{ required: true, message: 'Please input your Password!' }] },
        ]"
        type="password"
        placeholder="Password"
      >
        <a-icon slot="prefix" type="lock" style="color:rgba(0,0,0,.25)"/>
      </a-input>
    </a-form-item>
    <a-form-item>
      <a-button
        type="primary"
        html-type="submit"
        :disabled="hasErrors(form.getFieldsError())"
      >Log in</a-button>
    </a-form-item>
  </a-form>
</template>

<script>
function hasErrors(fieldsError) {
  return Object.keys(fieldsError).some(field => fieldsError[field]);
}
export default {
  data() {
    return {
      hasErrors,
      form: this.$form.createForm(this, { name: "horizontal_login" })
    };
  },
  mounted() {
    this.$nextTick(() => {
      this.form.validateFields();
    });
  },
  methods: {
    userNameError() {
      const { getFieldError, isFieldTouched } = this.form;
      return isFieldTouched("userName") && getFieldError("userName");
    },
    passwordError() {
      const { getFieldError, isFieldTouched } = this.form;
      return isFieldTouched("password") && getFieldError("password");
    },
    handleSubmit(e) {
      e.preventDefault();
      this.form.validateFields((err, values) => {
        if (!err) {
          console.log("Received values of form: ", values);
        }
      });
    }
  }
};
</script>

Conclusion

We can add form validation and change the layout with Ant Design Vue.

Categories
Ant Design Vue

Ant Design Vue — Date Picker and Form

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.

Extra Footer on the Date Picker

We can add an extra footer on the date picker by populating the renderExtraFooter slot:

<template>
  <div>
    <a-date-picker>
      <template slot="renderExtraFooter">extra footer</template>
    </a-date-picker>
  </div>
</template>

We can do the same with the a-range-picker , a-week-picker , and a-month-picker .

Date Picker Date Format

We can format the date picker’s date format our way with Moment:

<template>
  <div>
    <a-date-picker :default-value="moment('2020/01/01', dateFormat)" :format="dateFormat"/>
    <br>
    <a-date-picker
      :default-value="moment('01/01/2020', dateFormatList[0])"
      :format="dateFormatList"
    />
    <br>
    <a-month-picker :default-value="moment('2020/01', monthFormat)" :format="monthFormat"/>
    <br>
    <a-range-picker
      :default-value="[moment('2020/01/01', dateFormat), moment('2015/01/01', dateFormat)]"
      :format="dateFormat"
    />
  </div>
</template>
<script>
import moment from "moment";
export default {
  data() {
    return {
      dateFormat: "YYYY/MM/DD",
      monthFormat: "YYYY/MM",
      dateFormatList: ["DD/MM/YYYY", "DD/MM/YY"]
    };
  },
  methods: {
    moment
  }
};
</script>

We just set the string of the date format and it’ll be formatted.

Date Picker Size

The date picker size can be changed with the size prop:

<template>
  <div>
    <a-date-picker size="large"/>
    <br>
    <a-month-picker size="large" placeholder="Select Month"/>
    <br>
    <a-range-picker size="large"/>
    <br>
    <a-week-picker size="large" placeholder="Select Week"/>
  </div>
</template>
<script>
export default {};
</script>

Time Picker

We can add a time picker to a a-date-picker and a-range-picker with the show-time prop:

<template>
  <div>
    <a-date-picker show-time placeholder="Select Time" @change="onChange" @ok="onOk"/>
    <br>
    <a-range-picker
      :show-time="{ format: 'HH:mm' }"
      format="YYYY-MM-DD HH:mm"
      :placeholder="['Start Time', 'End Time']"
      @change="onChange"
      @ok="onOk"
    />
  </div>
</template>
<script>
export default {
  methods: {
    onChange(value, dateString) {
      console.log("Selected Time: ", value);
      console.log("Formatted Selected Time: ", dateString);
    },
    onOk(value) {
      console.log("onOk: ", value);
    }
  }
};
</script>

The show-time prop can take an object with the time format.

Form

We can add a simple form with the a-form component:

<template>
  <a-form :form="form" :label-col="{ span: 5 }" :wrapper-col="{ span: 12 }" @submit="handleSubmit">
    <a-form-item label="Note">
      <a-input
        v-decorator="['note', { rules: [{ required: true, message: 'Please input your note!' }] }]"
      />
    </a-form-item>
    <a-form-item label="Gender">
      <a-select
        v-decorator="[
          'gender',
          { rules: [{ required: true, message: 'Please select your gender!' }] },
        ]"
        placeholder="Select a option and change input text above"
        @change="handleSelectChange"
      >
        <a-select-option value="male">male</a-select-option>
        <a-select-option value="female">female</a-select-option>
      </a-select>
    </a-form-item>
    <a-form-item :wrapper-col="{ span: 12, offset: 5 }">
      <a-button type="primary" html-type="submit">Submit</a-button>
    </a-form-item>
  </a-form>
</template>

<script>
export default {
  data() {
    return {
      formLayout: "horizontal",
      form: this.$form.createForm(this, { name: "coordinated" })
    };
  },
  methods: {
    handleSubmit(e) {
      e.preventDefault();
      this.form.validateFields((err, values) => {
        if (!err) {
          console.log(values);
        }
      });
    },
    handleSelectChange(value) {
      this.form.setFieldsValue({
        note: `Hi, ${value === "male" ? "man" : "lady"}!`
      });
    }
  }
};
</script>

We add the a-input to add the input.

The v-decorator has the rules for validation for the field.

The this.form.setFieldsValue method sets the form field value.

The this.form reactive property is created by the this.$form.createForm method.

Conclusion

We can add forms and date pickers with the components provided by Ant Design Vue.

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.