本篇文章介绍一下element-ui框架中的form如何在设置label宽度为auto的情况下还能对齐表单项。
- label宽度设置为auto,那label的宽度就取决于字体本身的宽度,利用这个为出发点,我们可以获取到表单里的所有表单项的label宽度。如果label内容长度不一样,此时表单项是对不齐的,这时候我们可以获取表单项里面最宽的label长度,用这个宽度减去label宽度,设置marginLeft, 并设置类名为el-form-item__content的容器的marginLeft为最宽的label宽度,就能对齐了!
- 首先需要获取所有表单项的宽度
// 获取当前label宽度
getLabelWidth() {
if (this.$el && this.$el.firstElementChild) {
// 获取label宽度
const computedWidth = window.getComputedStyle(this.$el.firstElementChild).width;
return Math.ceil(parseFloat(computedWidth));
}
return 0;
},
- 问题来了,那在什么时候获取表单项宽度呢 我们知道只有dom元素渲染出来了才可以获取到元素宽度。所以在label组件中的mounted生命周期中进行表单项宽度的获取。
mounted() {
this.updateLabelWidth('update');
},
// 更新label宽度
updateLabelWidth(action = 'update') {
if (this.$slots.default && this.isAutoWidth && this.$el.firstElementChild) {
if (action === 'update') {
this.computedWidth = this.getLabelWidth();
} else if (action === 'remove') {
this.elForm.deregisterLabelWidth(this.computedWidth);
}
}
},
- 我们注意到获取的label宽度赋值给computedWidth属性,如果我们每次获取label宽度的时候就用一个数组push进去,每次数组发生变化时我们就可以计算出数组中最宽的label宽度。
watch: {
computedWidth(val, oldVal) {
if (this.updateAll) {
// 注册label宽度
this.elForm.registerLabelWidth(val, oldVal);
// 更新计算的label宽度
this.elFormItem.updateComputedLabelWidth(val, oldVal);
}
},
},
// 在form.vue文件中注册label宽度
registerLabelWidth(val, oldVal) {
if (val && oldVal) {
const index = this.getLabelWidthIndex(oldVal);
this.potentialLabelWidthArr.splice(index, 1, val);
} else if (val) {
this.potentialLabelWidthArr.push(val);
}
},
// 在 form-item.vue 文件中
// 更新计算的label宽度
updateComputedLabelWidth(width) {
this.computedLabelWidth = width ? `${width}px` : '';
},
- label宽度数组已经获取到了,接下来就需要计算label宽度数组(potentialLabelWidthArr)中的最大值
form.vue
computed: {
// 自动label宽度
autoLabelWidth() {
if (!this.potentialLabelWidthArr.length) return 0;
const max = Math.max(...this.potentialLabelWidthArr);
return max ? `${max}px` : '';
},
},
- 好了,数据齐全了,接下来需要处理label元素和类名为el-form-item__content的容器的样式了。
// 这里渲染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') {
// 关键在这行,label的marginLeft = 最大宽度 - label宽度
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];
}
},
// 这是类名为el-form-item__content的容器的样式
contentStyle() {
const ret = {};
const { label } = this;
if (this.form.labelPosition === 'top' || this.form.inline) return ret;
if (!label && !this.labelWidth && this.isNested) return ret;
const labelWidth = this.labelWidth || this.form.labelWidth;
// 设置marginleft
if (labelWidth === 'auto') {
if (this.labelWidth === 'auto') {
// 如果当前表单项设置了labelWidth
// 则设置content的marginlft为labelWidth
ret.marginLeft = this.computedLabelWidth;
} else if (this.form.labelWidth === 'auto') {
console.log('计算content marginLeft');
// 如果form对象的labelWidth 属性设置了auto,则设置它的marginLeft为label宽度数组的最大宽度(是个整数)
ret.marginLeft = this.elForm.autoLabelWidth;
}
} else {
// 如果没有设置为auto, 则取为当前设置的labelwidth
ret.marginLeft = labelWidth;
}
return ret;
},
总结:根据组件的单一性原则,label组件中获取到了label宽度,获取label宽度的时候进行监听,并在form组件中注册label长度,以及更新form-item组件中的label宽度(这个用于form-item的labelWidth属性设置为auto),由于label长度数组存储于form组件中,所以可以在这个组件中计算出label数组最大值,并在label组件中使用。