vue仿餓了麼寫的一個簡單的form組件

爲何寫這篇博客

不久前,朋友說掘金社區有不少精彩的文章能夠看(然而我來了後卻發現,大佬們的文章我大部分看不下去【太難了】,反而是掘金的沸點比較有趣,不少搞笑的程序員使我近期迷上了逛社區),可是,光看不發總以爲像是在白嫖。因此呢,趁着最近公司的任務比較輕鬆,我就正好把近期看的一些文章以及練手的小demo拿出來記錄一番,好讓各位大佬嘲笑個人才疏學淺吧,並且也證實了我並不是白嫖猿士,若是寫的不對的或者有更好寫法的地方,還要請各位看官可以細細提點......小女子天然是感激涕零css

開始動手

工欲善其事必先利其器,此次用到vue,因此讓咱們先把vue環境搭建起來吧(前提是電腦中已經安裝了node和npm) node下載地址,下載以後,點安裝,而後一直下一步直到完成。vue

<!--將下載源切換到淘寶,下載速度會快一點-->
npm config set registry https://registry.npm.taobao.org

<!--安裝vue-cli-->
npm i -g vue-cli

<!--建立並進入對應目錄-->
mkdir mycomponent && cd mycomponent

<!--用vue初始化項目-->
vue init webpack

<!--安裝依賴-->
npm i

<!--開始運行-->
npm run dev
複製代碼

到這裏,請打開瀏覽器,輸入localhost:8080,能看到一個vue的歡迎頁面,那麼恭喜你,能夠繼續看下去了。 爲了避免破壞vue官方提供的優美的歡迎頁,咱們利用路由來新增頁面。因此請你回到編輯器,進入路由目錄下的主文件src/router/index.jsnode

import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'

// 引入咱們的新頁面
import myForm from '@/components/myForm'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      name: 'HelloWorld',
      component: HelloWorld
    },
    // 定義新的路由項
    {
      path: '/myForm',
      name: 'myForm',
      component: myForm
    }
  ]
})

複製代碼

好了,新頁面的組件已經引入了,並且呢他的路由也寫好了,可是,這個頁面的組件咱們尚未建立噢,這時候請在components目錄下建立一個文件名稱爲myForm.vuewebpack

<template>
  <div>
    <h3>我是表單組件頁面</h3>
  </div>
</template>

<script>
export default {
  name: 'MyForm'
}
</script>

<style scoped>

</style>
複製代碼

這時候,測試下路由是否生效,以及咱們的頁面是否能顯示,須要在地址欄輸入:http://localhost:8080/#/myform
你的生沒生效我不知道,反正個人是成功了,若是你能看到下圖這個頁面,那咱們就繼續往下吧!程序員


開始進入正題了。
由於代碼過程須要使用到async-validator校驗器,因此須要安裝一下,能夠單獨安裝async-validator,也能夠直接安裝element-ui(包含了async-validator)

<!--方法1,安裝集成了async-validator的element-ui  新手推薦,由於這個框架有不少能夠學習的地方,我本人是新手,我用的也是這種-->
npm install element-ui --save-dev

<!--方法2,單獨安裝 新手不推薦,老手推薦-->
npm install async-validator --save-dev
複製代碼

接下來的過程表述起來對我來講太困難啦,爲了趕忙下班走人,因而乎我這邊就直接貼代碼啦!!!!!!!!!!!!! 各位大神見怪莫怪呀,若是有疑問的地方能夠問我,我懂得話確定告訴你喲!
根目錄下的main.js文件web

import Vue from 'vue'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import App from './App'
import router from './router'
Vue.config.productionTip = false
Vue.use(ElementUI)
/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})
複製代碼

src/router/index.jsvue-router

import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'

// 引入咱們的新頁面myform
import MyForm from '@/components/myForm'
Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      name: 'HelloWorld',
      component: HelloWorld
    },
    // 定義新的路由項
    {
      path: '/MyForm',
      name: 'MyForm',
      component: MyForm
    }
  ]
})
複製代碼

src/App.vuevue-cli

<template>
  <div id="app">
    <img src="./assets/logo.png">
    <router-view/>
  </div>
</template>

<script>
export default {
  name: 'App'
}
</script>

<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>
複製代碼

src/components/MyForm.vuenpm

<template>
  <div>
    <h3>我是表單組件頁面</h3>
    <my-form :model="ruleForm" :rules="rules" ref="myFrom">
      <my-form-item :label="label.username" prop="name">
        <my-input v-model="ruleForm.name"></my-input>
      </my-form-item>
      <my-form-item :label="label.password" prop="pwd">
        <my-input v-model="ruleForm.pwd" type="password"></my-input>
      </my-form-item>
      <my-form-item>
        <el-button type="primary" @click="submitForm">登陸</el-button>
      </my-form-item>
    </my-form>
    <div>{{ruleForm}}</div>
  </div>
</template>

<script>
import myInput from './form/input'
import myFormItem from './form/formItem'
import myForm from './form/form'
export default {
  name: 'MyForm',
  components: {
    myInput,
    myFormItem,
    myForm
  },
  data () {
    return {
      label: {
        username: '用戶名',
        password: '密碼'
      },
      ruleForm: {
        name: 'aa',
        pwd: ''
      },
      rules: {
        name: [
          {required: true, message: '用戶名不能爲空'},
          {min: 8, message: '用戶名必須大於8個字符'},
          {max: 16, message: '用戶名必須小於16個字符'}
        ],
        pwd: [
          {required: true, message: '密碼不能爲空'},
          {min: 8, message: '密碼必須大於8個字符'},
          {max: 16, message: '密碼必須小於16個字符'}
        ]
      }
    }
  },
  methods: {
    submitForm () {
      this.$refs.myFrom.validate(ret => {
        if (ret === false) {
          return false
        } else {
          alert('登陸成功')
        }
      })
    }
  }
}
</script>

<style scoped>

</style>


複製代碼

src/components/form/input.vueelement-ui

<template>
  <input :value="inputVal" @input="handleInputChange" :type="type">
</template>

<script>
export default {
  name: 'input',
  props: {
    value: {
      type: String,
      default: ''
    },
    type: {
      type: String,
      default: 'text'
    }
  },
  data () {
    return {
      inputVal: this.value
    }
  },
  methods: {
    // input框的值發生變化
    handleInputChange (e) {
      this.inputVal = e.target.value
      this.$emit('input', e.target.value)
      // 通知formItem組件進行校驗,顯示校驗結果
      this.$parent.$emit('doValidate', this.inputVal)
    }
  },
  created () {
  }
}
</script>

<style scoped>
input{
  line-height: 20px;
  padding: 6px 12px;
  background-color: #f5f5f5;
  border: none;
  border-radius: 4px;
  outline: none;
}
</style>
複製代碼

src/components/form/formItem.vue

<template>
  <div>
    <label v-if="label">{{label}}</label>
    <div>
      <!-- input -->
      <slot></slot>
      <!-- error info -->
      <p class="error-info" v-if="validateStatus == 'error'">{{errorMessage}}</p>
    </div>
  </div>
</template>

<script>
import Schema2 from 'async-validator'
export default {
  props: {
    // 表單名稱,通常是字符串的形式
    label: {
      type: String
    },
    // 校驗規則,數組的形式
    // [
    //   { required: true, message: '請輸入活動名稱', trigger: 'blur' },
    //   { min: 3, max: 5, message: '長度在 3 到 5 個字符', trigger: 'blur' }
    // ]
    prop: {
      type: String
    }
  },
  // 注入form 獲取model和rules
  inject: ['form'],
  data () {
    return {
      // 校驗狀態, 爲'error'或者''
      validateStatus: '',
      // 校驗失敗後的提示信息
      errorMessage: ''
    }
  },
  created () {
    // 監聽本身在子組件input裏面派發的事件doValidate,監聽到以後執行函數
    this.$on('doValidate', this.validateHandle)
  },
  // 掛載完畢執行
  mounted () {
    // 若是傳過來的校驗不爲空或者說有傳prop過來
    if (this.prop) {
      // 讓父組件觸發某個時間,而且將自身這個對象傳給父組件
      this.$parent.$emit('formItemAdd', this)
    }
  },
  methods: {
    validateHandle () {
      return new Promise(resolve => {
        // 將獲得的input裏面的最新值進行校驗(使用async-validate庫)須要安裝 npm install element-ui
        const descriptor = {[this.prop]: this.form.rules[this.prop]}
        // 上面已經引入了async-validate,接下將校驗規則descriptor當作參數,來new一個實例,而且使用實例的validate方法
        const validator = new Schema2(descriptor)
        // 兩個參數,第一個是你要校驗的鍵值{name: value},第二個是一個回調函數(判斷是否校驗經過)
        validator.validate({[this.prop]: this.form.model[this.prop]}, errors => {
          if (errors == null) {
            this.validateStatus = ''
            this.errorMessage = ''
            resolve(true)
          } else {
            this.validateStatus = 'error'
            this.errorMessage = errors[0].message
            resolve(false)
          }
        })
      })
    }
  }
}
</script>

<style>
.error-info{
  color: red;
}
</style>

複製代碼

src/components/form/form.vue

<template>
  <form>
    <slot></slot>
  </form>
</template>

<script>
export default {
  name: 'form',
  props: {
    model: {
      type: Object,
      required: true
    },
    rules: {
      type: Object
    }
  },
  provide () {
    return {
      form: this
    }
  },
  data () {
    return {
    }
  },
  created () {
    this.fields = []
    this.$on('formItemAdd', item => {
      this.fields.push(item)
    })
  },
  mounted () {
  },
  methods: {
    async validate (callback) {
      const tasks = this.fields.map(item => item.validateHandle())
      const results = await Promise.all(tasks)
      let ret = true
      results.forEach(val => {
        if (!val) {
          ret = false
        }
      })
      callback(ret)
    }
  }
}
</script>

<style scoped>

</style>


複製代碼

結語

感謝各位看官看到這裏!咱們下一次見

相關文章
相關標籤/搜索