新建form表单
传递的是响应式对象,直接把当前对象传递过去,this指代是当前表单的实例。
model:model作用:统一管理;prop:为了获取当前输入框中的值
开始校验
思考:(1)在什么地方通知校验?
KInput组件中通知,当数值发生变化时,会通知校验开始。
(2)如果触发事件为blur该怎样做?
???
告诉父组件该去做校验了
你是否第一时间想到的是这样做?
然而在KFormItem中,仍然是个slot,不能在slot中加事件
如果用event bus则强制用户必须设置一个bus(弃之)
所以是不是应该这样
通过父组件去派发事件
KFormitem中,自己派发自己监听
导入使用库,Schme:当前校验的规则
添加一个登录按钮
实现一个全局校验方法
思考:若用户不按套路出牌的情况下,以下代码将不健壮,该怎样做?
意想不到的事情发生了
思考:为什么会这样呢?
formItem中并不是所有的项都有prop属性,所以需要过滤一下
解决方法如下:
思考:以上写法中不严谨的地方,可以怎样改进?
(第一处)
(第二处)
实现弹框组件
弹窗这类组件的特点是它们在当前vue实例之外独立存在,通常挂载于body;它们是通过JS动态创建的,不需要在任何组件中声明。常⻅使用方式:
this.$create(Notice, {title: '村长喊你来搬砖',message: '提示信息',duration: 1000
}).show()
;
实现:
挂载是一种覆盖行为,不能直接mount(‘body’)
create函数:返回组件实例
通知组件
建通知组件,Notice.vue
{{title}}
{{message}}
export default {
props: {
title: {
type: String,
default: ""
}, message: {
type: String,
default: ""
},
duration: {
type: Number,
default: 1000
} },
data() {
return {
isShow: false
};
}, methods: {
show() {
this.isShow=true;
setTimeout(this.hide, this.duration);
},hide() { this.isShow = false; this.remove();} }
};
.box { position: fixed; width: 100%; top: 16px; left: 0; text-align: center; pointer-events: none; background-color: #fff; border: grey 3px solid; box-sizing: border-box;}.box-content {width: 200px; margin: 10px auto; font-size: 14px; padding: 8px 16px; background: #fff; border-radius: 3px; margin-bottom: 8px;}
使用
关于作业
思考拓展
1、修正input中$parent写法的问题
跟组件从上往下去广播?,从跟组件自上而下去广播,告诉大家发生了什么事件,让大家去做监听
从下网上,从里往外
2、学习element源码
链接地址:github.com/ElemenFE/element
思考问题讲解:
1、修正input中$parent写法问题
修正formItem中因父组件找不到问题
(1)mixin emitter
(2)声明componentName
(3)dispatch()
广播:自上而下派发事件
function broadcast(componentName, eventName, params) {
//遍历所有的子元素,如果子元素componentName和传入的相同则派发事件
this.$children.forEach(child => {
var name = child.$options.componentName;
if (name === componentName) {
child.$emit.apply(child, [eventName].concat(params));
} else {
broadcast.apply(child, [componentName, eventName].concat([params]));
}
});
}
export default {
methods: {
//冒泡查找compantName相同的组件并派发事件
dispatch(componentName, eventName, params) {
var parent = this.$parent || this.$root;
var name = parent.$options.componentName;
//向上查找直到找到相同名称的组件
while (parent && (!name || name !== componentName)) {
parent = parent.$parent;
if (parent) {
name = parent.$options.componentName;
}
}
//如果找到就派发事件
if (parent) {
//通过apply的方式指定上下文,所有的参数会用concat进行拼接,结果依然是个数组
parent.$emit.apply(parent, [eventName].concat(params));
}
},
broadcast(componentName, eventName, params) {
broadcast.call(this, componentName, eventName, params);
}
}
};
修改$children
element源码参考:
import objectAssign from 'element-ui/src/utils/merge';
export default {
name: 'ElForm',
componentName: 'ElForm',
provide() {
return {
elForm: this
};
},
props: {
model: Object,
rules: Object,
labelPosition: String,
labelWidth: String,
labelSuffix: {
type: String,
default: ''
},
inline: Boolean,
inlineMessage: Boolean,
statusIcon: Boolean,
showMessage: {
type: Boolean,
default: true
},
size: String,
disabled: Boolean,
validateOnRuleChange: {
type: Boolean,
default: true
},
hideRequiredAsterisk: {
type: Boolean,
default: false
}
},
watch: {
rules() {
// remove then add event listeners on form-item after form rules change
this.fields.forEach(field => {
field.removeValidateEvents();
field.addValidateEvents();
});
if (this.validateOnRuleChange) {
this.validate(() => {});
}
}
},
computed: {
autoLabelWidth() {
if (!this.potentialLabelWidthArr.length) return 0;
const max = Math.max(...this.potentialLabelWidthArr);
return max ? `${max}px` : '';
}
},
data() {
return {
fields: [],
potentialLabelWidthArr: [] // use this array to calculate auto width
};
},
created() {
this.$on('el.form.addField', (field) => {
if (field) {
this.fields.push(field);
}
});
/* istanbul ignore next */
this.$on('el.form.removeField', (field) => {
if (field.prop) {
this.fields.splice(this.fields.indexOf(field), 1);
}
});
},
methods: {
resetFields() {
if (!this.model) {
console.warn('[Element Warn][Form]model is required for resetFields to work.');
return;
}
this.fields.forEach(field => {
field.resetField();
});
},
clearValidate(props = []) {
const fields = props.length
? (typeof props === 'string'
? this.fields.filter(field => props === field.prop)
: this.fields.filter(field => props.indexOf(field.prop) > -1)
) : this.fields;
fields.forEach(field => {
field.clearValidate();
});
},
validate(callback) {
if (!this.model) {
console.warn('[Element Warn][Form]model is required for validate to work!');
return;
}
let promise;
// if no callback, return promise
if (typeof callback !== 'function' && window.Promise) {
promise = new window.Promise((resolve, reject) => {
callback = function(valid) {
valid ? resolve(valid) : reject(valid);
};
});
}
let valid = true;
let count = 0;
// 如果需要验证的fields为空,调用验证时立刻返回callback
if (this.fields.length === 0 && callback) {
callback(true);
}
let invalidFields = {};
this.fields.forEach(field => {
field.validate('', (message, field) => {
if (message) {
valid = false;
}
invalidFields = objectAssign({}, invalidFields, field);
if (typeof callback === 'function' && ++count === this.fields.length) {
callback(valid, invalidFields);
}
});
});
if (promise) {
return promise;
}
},
validateField(props, cb) {
props = [].concat(props);
const fields = this.fields.filter(field => props.indexOf(field.prop) !== -1);
if (!fields.length) {
console.warn('[Element Warn]please pass correct props!');
return;
}
fields.forEach(field => {
field.validate('', cb);
});
},
getLabelWidthIndex(width) {
const index = this.potentialLabelWidthArr.indexOf(width);
// it's impossible
if (index === -1) {
throw new Error('[ElementForm]unpected width ', width);
}
return index;
},
registerLabelWidth(val, oldVal) {
if (val && oldVal) {
const index = this.getLabelWidthIndex(oldVal);
this.potentialLabelWidthArr.splice(index, 1, val);
} else if (val) {
this.potentialLabelWidthArr.push(val);
}
},
deregisterLabelWidth(val) {
const index = this.getLabelWidthIndex(val);
this.potentialLabelWidthArr.splice(index, 1);
}
}
};
自己实现:
2、使用vue.extend方式实现create方法
Vue.extend
const comp={data:{},props:{}}
const Ctor=Vue.extend(comp)
new Ctor({propsData:{}})