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
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.