In the last chapter we talked about Element’s ICONS, buttons, and links. In this chapter we’ll talk about forms
The form
The form is a little bit more code, and I’ll parse it step by step
Form
The first is the Created method of the Form component, which binds two events, one to add a field and one to delete a field
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); }});Copy the code
When FormItem executes a Mounted callback, the FormItem instance is stored by the event that triggers this
The four main methods on the Form component validate rules, validateField validate partial rules, resetFields reset checksum data, and clearValidate reset validates
Each of the four methods iterates through the child FormItem (aka field) and triggers its logic to process it, ValidateField and clearValidate filter FormItem options through files.filter (field => ~props. IndexOf (field.prop))
FormItem
mounted() {
if (this.prop) {
this.dispatch('ElForm'.'el.form.addField'[this]);
let initialValue = this.fieldValue;
if (Array.isArray(initialValue)) {
initialValue = [].concat(initialValue);
}
Object.defineProperty(this.'initialValue', {
value: initialValue
});
this.addValidateEvents(); }},beforeDestroy() {
this.dispatch('ElForm'.'el.form.removeField'[this]);
}
Copy the code
The FormItem performs the dispatch method, which is mixed in through mixins, and the more interesting way to trigger the parent Form
methods: {
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) { parent.$emit.apply(parent, [eventName].concat(params)); }}}Copy the code
By looking up through the while, if there is a component whose name value is ElForm, that is, the Form component, its bound add/remove field event is executed
After the current FormItem instance is stored in the Form, fieldValue is added to the instance with initialValue as the key, because it is immutable by default as defined through Object.defineProperty, Obtain the corresponding prop of the model attribute of the Form through the prop attribute, and then monitor the change of data through the calculation attribute. The writing method of prop can be XXX or XXX: XXX or XXX.xxx
The value of fieldValue is processed by getPropByPath, which transforms path
/\[(\w+)\]/g => '.$1'
Copy the code
/ / ^ \. = > 'Copy the code
// getPropByPath returns the value
return {
o: tempObj, // Prop points to the object of the value
k: keyArr[i], // The key that prop points to
v: tempObj ? tempObj[keyArr[i]] : null // the value that prop points to
};
Copy the code
After processing, split(‘.’) the path to get the children of the model, such as path=’input.el.text’ corresponds to model.input.el.text, so fieldValue returns the corresponding value
fieldValue() {
const model = this.form.model;
if(! model || !this.prop) { return; }
let path = this.prop;
if (path.indexOf(':')! = = -1) {
path = path.replace(/ :.'. ');
}
return getPropByPath(model, path, true).v;
}
Copy the code
This.addvalidateevents () specifies whether rules are required or whether the parent component has rules
If one of the criteria is met, two events are added to listen for changes to input fields, checkboxes, and other form elements
addValidateEvents() {
const rules = this.getRules(); // Get the form rule
if (rules.length || this.required ! = =undefined) {
this.$on('el.form.blur'.this.onFieldBlur);
this.$on('el.form.change'.this.onFieldChange); }}Copy the code
These events are emitted when child components such as Input and Radio lose focus or change
Triggered after these events, will perform this. Validate (‘ blur ‘| |’ change ‘), and validate according to the first parameter to filter rules, getFilteredRule function according to the trigger is’ blur ‘| |’ change ‘filter
As can be seen, trigger supports two writing methods: string and array. The filtered rules are being shallow copied, and objectAssign is the polyfill of Object.assign
getFilteredRule(trigger) {
const rules = this.getRules();
return rules.filter(rule= > {
if(! rule.trigger || trigger ===' ') return true;
if (Array.isArray(rule.trigger)) {
return rule.trigger.indexOf(trigger) > -1;
} else {
return rule.trigger === trigger;
}
}).map(rule= > objectAssign({}, rule));
}
Copy the code
Store filtered rules into an object descriptor = {[this.prop]: Rules}, Element uses the async-validator library. It takes a rule object, rules, and uses it to generate validators to verify data. It calls the validate function and returns an error message if it does not match the rules. If the component has a Form, it executes the validate method attached to the Form. Unfortunately, I don’t see this method. If you want to trigger a single Form item for validation, you can use this validate method
validate(trigger, callback = noop) {
this.validateDisabled = false;
const rules = this.getFilteredRule(trigger);
if((! rules || rules.length ===0) && this.required === undefined) {
callback();
return true;
}
this.validateState = 'validating';
const descriptor = {};
if (rules && rules.length > 0) {
rules.forEach(rule= > {
delete rule.trigger;
});
}
descriptor[this.prop] = rules;
const validator = new AsyncValidator(descriptor);
const model = {};
model[this.prop] = this.fieldValue;
validator.validate(model, { firstFields: true }, (errors, invalidFields) = > {
this.validateState = ! errors ?'success' : 'error';
this.validateMessage = errors ? errors[0].message : ' ';
callback(this.validateMessage, invalidFields);
this.elForm && this.elForm.$emit('validate'.this.prop, ! errors,this.validateMessage || null);
});
}
Copy the code
ClearValidate is also very simple to clean up state
clearValidate() {
this.validateState = ' ';
this.validateMessage = ' ';
this.validateDisabled = false;
}
Copy the code
ResetField gets the relevant object and key via the getPropByPath function, and restores the initialValue via obj[key] = this.initialValue
The TimeSelect component is special and needs to trigger its fieldReset event, which I’ll talk about later
LabelWrap
This component is the label of the FormItem, which is implemented with Render and gets the autoLabelWidth (left margin) of the parent Form using getComputedStyle(this.$el.firstelementChild). Continuously calculate the text width and adapt the label
render() {
const slots = this.$slots.default;
if(! slots)return null;
if (this.isAutoWidth) {
const autoLabelWidth = this.elForm.autoLabelWidth;
const style = {};
if(autoLabelWidth && autoLabelWidth ! = ='auto') {
const marginLeft = parseInt(autoLabelWidth, 10) - this.computedWidth;
if (marginLeft) {
style.marginLeft = marginLeft + 'px'; }}return (<div class="el-form-item__label-wrap" style={style}>
{ slots }
</div>);
} else {
return slots[0]; }}Copy the code
Tired, I will write here