field-input component for vee-validate

by gongzza

HTML

<script src="https://unpkg.com/[email protected]/dist/vee-validate.js"></script>
<script src="https://unpkg.com/[email protected]/dist/locale/ko.js"></script>

<div id="app">
  <form class="ui form" @submit.prevent="onSubmit">
    <field-input field="regist.email" type="text" rules="required|email" v-model="user.email"></field-input>
    <field-input field="regist.name" type="text" rules="required" v-model="user.name"></field-input>
    <field-input field="regist.password" type="password" rules="required|min:6" v-model="user.password"></field-input>
    <field-input field="regist.password_confirmed" type="password" rules="required|confirmed:password" v-model="user.password_confirmed"></field-input>
    <field-input field="regist.homepage" v-model="user.homepage" rules="url"></field-input>
    <button type="submit" class="ui submit button">Submit</button>
  </form>
</div>

<template id="field-input">
  <div class="field" :class="{error: errors.has(fieldName, scope), required: isRequired}">
    <label v-text="prettyName"></label>
    <input :type="type" :name="fieldName" :value="value" @input="updateValue($event.target.value)">
    <span class="error" v-if="errors.has(fieldName, scope)">
      {{ errors.first(fieldName, scope) }}
    </span>
  </div>
</template>

CSS

@import url('https://unpkg.com/[email protected]/semantic.css');

span.error {
  color: #9F3A38;
}

JavaScript

const attrs = {
  email: '이메일',
  name: '이름',
  password: '패스워드',
  password_confirmed: '패스워드 확인',
  homepage: '홈페이지'
}

Vue.component('field-input', {
  name: 'field-input',
  template: '#field-input',
  inject: ['$validator'],
  props: {
    value: {
      required: true
    },
    field: {
      type: String,
      require: true
    },
    rules: [String, Object],
    type: {
      type: String,
      default: 'text'
    },
  },
  computed: {
    prettyName() {
      return attrs[this.fieldName]
    },
    fieldName() {
      return this.field.includes('.')
        ? this.field.substring(this.field.indexOf('.') + 1)
        : this.field
    },
    scope() {
      return this.field.includes('.')
        ? this.field.substring(0, this.field.indexOf('.'))
        : '__global__'
    },
    isRequired() {
      if (!this.rules) {
        return
      }

      return (typeof this.rules === 'string')
        ? this.rules.includes('required')
        : this.rules['required']
    }
  },
  methods: {
    updateValue(val) {
      this.rules && this.$validator.validate(this.fieldName, val, this.scope)
      this.$emit('input', val)
    },
    getter() {
      return this.value
    },
    context() {
      return this
    }
  },
  mounted() {
    const options = {
      scope: this.scope,
      getter: this.getter,
      context: this.context,
      prettyName: this.prettyName
    }
    this.$validator.attach(this.fieldName, this.rules, options)
  },
  destroyed() {
    this.$validator.detach(this.fieldName)
  },
})

Vue.use(VeeValidate)

const dictionary = {
  ko: {
    messages: {
      email: () => `올바른 이메일 형식이 아닙니다.`,
      required: (field) => `필수 입력란입니다.`,
      confirmed: (field, confirmedField) => `${confirmedField}와 일치하지 않습니다.`,
      url: () => `올바른 URL 형식이 아닙니다.`
    }
  }
}

VeeValidate.Validator.updateDictionary(dictionary)
VeeValidate.Validator.setLocale('ko')
new Vue({
  el: '#app',
  data() {
    return {
    	user: {
      	name: '',
      	email: '',
    ...