用以收集、校验、提交数据。
Form:作为数据收集和数据校验数据
一、props
二、event:
三、slot
FormItem:作为校验单项表单
一、props
二、event:
三、slot
// 用例data() { return { info: { username: '', password: '' }, rules: { username: [ {required: true, message: '用户名不能为空', trigger: 'blur'}, ], password: [ {required: true, message: '密码不能为空', trigger: 'blur'}, ] } } }
Form
FormItem
一、问题:
带着上面的思路来分析下面的 form 表单
- // App.js
"form" :model="info" :rules="rules"> - <i-form-item prop="username">
- <i-input v-model="info.username">i-input>
- i-form-item>
- <i-form-item prop="password">
- <i-input v-model="info.password">i-input>
- i-form-item>
-
-
- data() {
- return {
- info: {
- username: '',
- password: ''
- },
- rules: {
- username: [
- {required: true, message: '用户名不能为空', trigger: 'blur'},
- ],
- password: [
- {required: true, message: '密码不能为空', trigger: 'blur'},
- ]
- }
- }
- },
- methods: {
- handleSubmit() {
- this.$refs.form.validate((valid) => {
- if (valid) {
- window.alert('成功');
- } else {
- window.alert('表单校验失败');
- }
- })
- }
- }
- // Form.vue
- <form>
- <slot>slot>
- form>
-
- <script>
- export default {
- name: 'iForm',
- provide() {
- return {
- form: this
- }
- },
- props: {
- model: {
- type: Object
- },
- rules: {
- type: Object
- }
- },
- beforeCreate() {
- console.log('form-beforeCreate');
- },
- created() {
- this.$on('on-form-item-add', (field) => {
- if (field) this.fields.push(field);
- })
- this.$on('on-form-item-remove', (field) => {
- if (field.prop) {
- this.fields.splice(this.fields.indexOf(field), 1);
- }
- })
- },
- methods: {
- resetFields() {
- this.fields.forEach(field => {
- field.resetFields();
- })
- },
- validate(callback) {
- return new Promise(resolve => {
- let valid = true;
- let count = 0;
- this.fields.forEach(field => {
- field.validate('', errors => {
- if (errors) {
- valid = false;
- }
- if (++count === this.fields.length) {
- // 全部完成
- resolve(valid);
- if (typeof callback === 'function') {
- callback(valid);
- }
- }
- })
- })
- })
- }
- },
- data() {
- return {
- fields: []
- }
- },
- mounted() {
- }
- }
- script>
-
- <style>
-
- style>
- // formItem.vue
- <div>
- <label v-if="label" :class="{ 'i-form-item-label-required': isRequired }">{{label}}label>
- <div>
- <slot>slot>
- <div v-if="validateState === 'error'" class="i-form-item-message">{{ validateMessage }}div>
- div>
- div>
-
- <script>
- import AsyncValidator from 'async-validator';
- import Emitter from '../../mixins/emitter';
- export default {
- name: 'iFormItem',
- mixins: [ Emitter ],
- inject: [
- 'form'
- ],
- props: {
- label: {
- type: String,
- default: ''
- },
- // username 还需要拿到对应的值
- prop: {
- type: String
- }
- },
- // 组件渲染时,将实例缓存在 Form 中
- mounted() {
- if (this.prop) {
- this.dispatch('iForm', 'on-form-item-add', this);
-
- this.initialValue = this.fieldValue;
-
- this.setRules;
- }
- },
- beforeDestroy() {
- this.dispatch('iForm', 'on-form-item-remove', this);
- },
- data() {
- return {
- validateState: '',
- validateMessage: '',
- isRequired: false
- }
- },
- computed: {
- // 从 Form 的 model 中动态得到当前表单组件的数据
- fieldValue() {
- return this.form.model[this.prop];
- }
- },
- methods: {
- setRules() {
- let rules = this.getRules();
- if (rules.length) {
- rules.every((rule) => {
- // 如果当前校验规则中有必填项,则标记出来
- this.isRequired = rule.required;
- })
- }
- this.$on('on-form-blur', this.onFieldBlur);
- this.$on('on-form-change', this.onFieldChange);
- },
- onFieldBlur() {
- console.log('onFieldBlur');
- // 接收到改变之后进行校验
- this.validate('blur');
- },
- onFieldChange() {
- console.log('onFieldChange');
- this.validate('change');
- },
- // 从 Form 的 rules 属性中,获取当前 FormItem 的校验规则
- getRules() {
- let formRules = this.form.rules;
- formRules = formRules ? formRules[this.prop] : [];
- return [].concat(formRules || []);
- },
- // 只支持 blur 和 change, 过滤出符合要求的 rule 规则
- getFilteredRule (trigger) {
- const rules = this.getRules();
- return rules.filter(rule => !rule.trigger || rule.trigger.indexOf(trigger) !== -1);
- },
- // 重置数据
- resetField() {
- this.validateState = '';
- this.validateMessage = '';
- this.form.model[this.prop] = this.initialValue;
- },
- validate(trigger, callback = function() {}) {
- // 交给数据校验 看看是不是符合
- // 查看类型
- let rules = this.getFilteredRule(trigger);
-
- if (!rules || rules.length === 0) {
- return true;
- }
-
- // 设置状态
- this.validateState = 'validating';
-
- // 调用别人库来进行校验
- let descriptor = {};
- descriptor[this.prop] = rules;
-
- const validator = new AsyncValidator(descriptor);
- let model = {};
- model[this.prop] = this.fieldValue;
- console.log(model);
- validator.validate(model, { firstFields: true }, errors => {
- this.validateState = !errors ? 'success' : 'error';
- this.validateMessage = errors ? errors[0].message : '';
- callback(this.validateMessage);
- })
- }
- }
- }
- script>
- <style>
- .i-form-item-label-required:before {
- content: '*';
- color: red;
- }
- .i-form-item-message {
- color: red;
- }
- style>
- // input.vue
- <input
- type="text"
- :value="currentValue"
- @input="handleInput"
- @blur="handleBlur">
- template>
-
- <script>
- import Emitter from '../../mixins/emitter';
- export default {
- name: 'iInput',
- mixins: [ Emitter ],
- props: {
- value: {
- Type: String,
- default: ''
- }
- },
- data() {
- return {
- currentValue: this.value
- }
- },
- watch: {
- value(newVal) {
- this.currentValue = newVal;
- }
- },
- methods: {
- handleInput(event) {
- const value = event.target.value;
- this.currentValue = value;
- this.$emit('input', value);
- this.dispatch('iFormItem', 'on-form-change', value);
- },
- handleBlur() {
- this.dispatch('iFormItem', 'on-form-blur', this.currentValue);
- }
- }
- }
- script>
-
- <style>
-
- style>
- // emitter.js
- function broadcast(componentName, eventName, params) {
- this.$children.forEach(child => {
- const name = child.$options.name;
-
- if (name === componentName) {
- child.$emit.apply(child, [eventName].concat(params));
- } else {
- broadcast.apply(child, [componentName, eventName].concat([params]));
- }
- });
- }
- export default {
- methods: {
- dispatch(componentName, eventName, params) {
- 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) {
- parent.$emit.apply(parent, [eventName].concat(params));
- }
- },
- broadcast(componentName, eventName, params) {
- broadcast.call(this, componentName, eventName, params);
- }
- }
- };