仿 ELEMENTUI 实现一个简单的 Form 表单

  原文:仿 ElmentUI 实现一个 Form 表单

 

一、目标

  ElementUI 中 Form 组件主要有以下 功能 / 模块:

  • Form
  • FormItem
  • Input
  • 表单验证

  在这套组件中,有 3 层嵌套,这里面使用的是 slot 插槽,我们在接下来的代码中也需要运用到它。

 

二、组件设计

  • e-form 全局校验,并提供插槽;
  • e-form 单一项校验和显示错误信息,并提供插槽;
  • e-input 负责数据的双向绑定

 

三、开始

e-input

<template>
  <div>
    <!-- 1.绑定 value 2.响应 input 事件 -->
    <input type="text" :value="valueInInput" @input="handleInput">
  </div>
</template>

<script>
export default {
  name: 'EInput',
  props: {
    value: {
      type: String,
      default: ''
    }
  },
  data() {
    return {
      valueInInput: this.value // 确保数据流的单向传递
    }
  },
  methods: {
    handleInput(e) {
      this.valueInInput = e.target.value;
      this.$emit('input', this.valueInInput); // 此处提交的事件名必须是 ‘input’

      // 数据变了,定向通知 formItem 进行校验
      this.dispatch('EFormItem', 'validate', this.valueInInput);
    },

    dispatch(componentName, eventName, params) { // 查找指定 name 的组件,
      let parent = this.$parent || this.$root;
      let name = parent.$options.name

      while(parent && (!name || name !== componentName)) {
        parent = parent.$parent;
        if (parent) {
          name = parent.$options.name;
        }
      }
      if (parent) {
        // 这里,我们不能用 this.$emit 直接派发事件,因为在 FormItem 组件中,Input 组件的位置只是一个插槽,无法做事件监听,
        // 所以此时我们让 FormItem 自己派发事件,并自己监听。修改 FormItem 组件,在 created 中监听该事件。
        parent.$emit.apply(parent, [eventName].concat(params));
      }
    }
  }
}
</script>

 这里需要注意的是 v-model 绑定的值与 props 传递的值的关系,从而能将修改后的值暴露至顶层自定义组件。使用如下:

<template>
  <div id="app">
    <e-input v-model="initValue"></e-input>
    <div>{{ initValue }}</div>
  </div>
</template>

<script>
import EInput from './components/Input.vue';

export default {
  name: "app",
  components: {
    EInput
  },
  data() {
    return {
      initValue: '223',
    };
  },
};
</script>

 

FormItem 的设计

<template>
  <div>
    <label v-if="label">{{ label }}</label>
    <div>
      <!-- 拓展槽 -->
      <slot></slot>
      <!-- 验证提示信息 -->
      <p v-if="validateState === 'error'" class="error">{{ validateMessage }}</p>
    </div>
  </div>
</template>

<script>
import AsyncValidator from 'async-validator';

export default {
  name: 'EFormItem',
  props: {
    label: { type: String, default: '' },  // 表单项的名称
    prop: { type: String, default: '' } // 表单项的自定义 prop
  },
  inject: ['eForm'], // provide/inject,vue 跨层级通信
  data() {
    return {
      validateState: '',
      validateMessage: ''
    }
  },
  methods: {
    validate() {
      return new Promise(resolve => {
        const descriptor = {}; // async-validator 建议用法;
        descriptor[this.prop] = this.eForm.rules[this.prop];
        // 校验器
        const validator = new AsyncValidator(descriptor);
        const model = {};
        model[this.prop] = this.eForm.model[this.prop];
        // 异步校验
        validator.validate(model, errors => {
          if (errors) {
            this.validateState = 'error';
            this.validateMessage = errors[0].message;
            resolve(false);
          } else {
            this.validateState = '';
            this.validateMessage = '';
            resolve(true);
          }
        })
      })
    },
    dispatch(componentName, eventName, params) { // 查找上级指定 name 的组件
      var parent = this.$parent || this.$root;
      var name = parent.$options.name;

      while (parent && (!name || name !== componentName)) {
        parent = parent.$parent;

        if (parent) {
          name = parent.$options.name;
        }
      }
      if (parent) {
        parent.$emit.apply(parent, [eventName].concat(params));
      }
    }
  },
  created() {
    this.$on('validate', this.validate); // 'validate' 事件由 e-input 组件通知,在 e-form-item 组件接收到后自行触发对应方法
  },
  // 因为我们需要在 Form 组件中校验所有的 FormItem ,
  // 所以当 FormItem 挂载完成后,需要派发一个事件告诉 Form :你可以校验我了。
  mounted() {
    // 当 FormItem 中有 prop 属性的时候才校验,
    // 没有的时候不校验。比如提交按钮就不需要校验。
    if (this.prop) {
      this.dispatch('EForm', 'addFiled', this);
    }
  }
}
</script>

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

 其中, methods 中的方法均是辅助方法,validate() 是异步校验的方法。

 

Form 的设计

 

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

<script>
export default {
  name: 'EForm',
  props: {
    model: {
      type: Object,
      required: true
    },
    rules: {
      type: Object
    }
  },
  provide() { // provide/inject,vue 跨层级通信
    return {
      eForm: this // form 组件实例, 在其他组件中可以通过 this.xxx 来获取属性/方法
    }
  },
  data() {
    return {
      fileds: [] // 需要校验的 e-form-item 组件数组
    }
  },
  methods: {
    async validate(cb) {
      const eachFiledResultArray = this.fileds.map(filed => filed.validate());

      const results = await Promise.all(eachFiledResultArray);
      let ret = true;
      results.forEach(valid => {
        if (!valid) {
          ret = false;
        }
      });
      cb(ret)
    }
  },
  created() {
    // 缓存需要检验的组件
    this.fileds = [];
    this.$on('addFiled', filed => this.fileds.push(filed))
  }
}
</script>

 

原文地址:https://www.cnblogs.com/cc-freiheit/p/10789309.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


el-menu 有个属性 :default-active="curActive"  el-menu-item有个属性:index=“home”这2个属性值对上号就自动定位了data(){ return{ curActive:"home" }; },
基础用法1<el-inputv-model="input1"palcehoder="请输入"></el-input>23varMain={4data(){5return{6input1:''7}8}9}10varCtor=Vue.extend(Main)11newCtor().$mount('#app&#03
 1.安装element-uinpminstallelement-ui-S 2.在main.js中importElementUIfrom'element-ui'import'element-ui/libheme-chalk/index.css'Vue.use(ElementUI)注意index.css的路径不要出错:在node_modules文件夹里node_modules\element-ui\lib\theme-c
layout布局通过基础的24分栏,可进行快速布局基础布局使用单一分栏创建基础的栅格布局,通过span属性指定每栏的大小<el-col:span="8"></el-col>1<el-row>2<el-col:span="8"><divclass="grid-contentbg-purple"></div>&
 今天做一个选择年份的功能,直接调用了ElementUI里面的DatePicker组件,官网上有该组件的用法介绍,讲得很清楚。 我的代码: 官网说明: 奇怪的事情发生了,我明明按照例子写了  value-format="yyyy" 可是获得的值却一直还是Date对象 仔细检查后,我突然发现我的v-model
  that.end 即为结束日期
vueelementUitree懒加载使用详情2018年11月21日11:17:04开心大表哥阅读数:3513 https://blog.csdn.net/a419419/article/details/84315751 背景:vue下使用elementUI文档:http://element-cn.eleme.io/#/zh-CN/componentree#tree-shu-xing-kong-jian需求:只保存二
环境搭建说明:1、全局安装angluar脚手架npminstall-g@angular/cli2、初始化项目(支持scss)ngnew项目名称--style=scss//进入项目cd项目名称运行代码可以是:serve或者npminstall(安装依赖)npmstart(运行)3、安装elementnpm
1、在写埋点监控项目的时候,需求是表格里面的数据后台传递过来为0,但是要求显示的时候为—,在elementUI判断,将prop去掉在下面加上<templateslot-scope="scope"><emplate>里面在写vue的判断,因为通过Scopedslot可以获取到row,column,$index和store(table内容的状态管理)的数据。2、
<el-table-columnprop="pubArea"//表格data中对应的字段column-key="pubArea"//过滤条件变化时根据此key判断是哪个表头的过滤label="报修类型"align="center"width=
1.npminstallbabel-plugin-component-D   然后.babelrc替换plugins 文件就在根目录下  2.组件中英文及自定义中英文importVueI18nfrom'vue-i18n'importenLocalefrom'element-ui/lib/locale/lang/en'importzhLocalefrom'element-ui/lib/locale/lang/zh-C
 <el-treeref="tree":data="menu.treeData":props="menu.defaultProps":filter-node-method="filterNode":expand-on-click-node=&quot
2019独角兽企业重金招聘Python工程师标准>>>使用vue+elementui的tree组件,elementui官网elementui的tree组件问题描述:tree层级过多时左右不可滚动问题解决:修改overflow属性值.el-tree-node>.el-tree-node_children{overflow:visible;} 其他相关链接:css--ov
首先elementUI的导航栏中的选中项的高亮显示时的字体颜色可以在属性中设置,但是高亮时的背景颜色不能设置,所以要自己修改高亮的背景颜色.el-menu-item.is-active{background-color:#00b4aa!important;} 在使用elementUI构建vue项目的时候会遇到页面刷新的时候子路由会保
1.首先创建Vue项目(使用vue-cli进行创建)创建项目文章指导地址: https://blog.csdn.net/assiduous_me/article/details/892086492.ElementUI:一套为开发者、设计师和产品经理准备的基于Vue2.0的桌面端组件库 链接地址:http://element.eleme.io/#/zh-CN3.正式开
使用vue开发项目,用到elementUI,根据官网的写法,我们可以自定义主题来适应我们的项目要求,下面来介绍一下两种方法实现的具体步骤,(可以参考官方文档自定义主题官方文档),先说项目中没有使用scss编写,用主题工具的方法(使用的较多)第一种方法:使用命令行主题工具使用vue-cli安装完项目并引
每页显示的序号都是一样的:<el-table:data="tableData"highlight-current-row@current-change="handleCurrentChange"><el-table-columntype="index"width="50"></el-table-column><el-table>根据
  记录一下自己踩的坑,控制element内的table的某列显示隐藏不能用v-show,而是要用v-if具体网上百度了,看一下v-show和v-if的区别吧猜测:由于el-table-column会生成多行标签元素,根据v-show是不支持template语法的,推断v-show不能显示隐藏多个元素 
样式重置单vue文件中重置全局重置多写一个style不加scrop,可能会影响全局样式待补充table单价数量小计3252?在ElementUI中,我需要获取row内的数据并进行计算,需要用到v-slot<el-table-columnlabel="小计"><templatev-slot="scope">{{scop
工作需要做一个带滑块效果的导航栏,初步想法是用element的导航组件去做,后面摸坑结合各位大佬的博客初步实现效果,话不多说,直接上代码,记录一下爬坑之旅1<template>2<divclass="y-nav">3<el-rowclass="nav">4<el-menu5:default-active="$route.