Skip to content

Commit a408b3d

Browse files
authored
Form: label-width supports width auto (#14944)
1 parent e7fdd3d commit a408b3d

File tree

9 files changed

+177
-16
lines changed

9 files changed

+177
-16
lines changed

examples/docs/en-US/form.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -593,7 +593,7 @@ All components in a Form inherit their `size` attribute from that Form. Similarl
593593
| rules | validation rules of form | object |||
594594
| inline | whether the form is inline | boolean || false |
595595
| label-position | position of label. If set to 'left' or 'right', `label-width` prop is also required | string | left / right / top | right |
596-
| label-width | width of label, and all its direct child form items will inherit this value | string |||
596+
| label-width | width of label, e.g. '50px'. All its direct child form items will inherit this value. Width `auto` is supported. | string |||
597597
| label-suffix | suffix of the label | string |||
598598
| hide-required-asterisk | whether required fields should have a red asterisk (star) beside their labels | boolean || false |
599599
| show-message | whether to show the error message | boolean || true |
@@ -623,7 +623,7 @@ All components in a Form inherit their `size` attribute from that Form. Similarl
623623
| ---- | ----| ---- | ---- | ---- |
624624
| prop | a key of `model`. In the use of validate and resetFields method, the attribute is required | string | keys of model that passed to `form` |
625625
| label | label | string |||
626-
| label-width | width of label, e.g. '50px' | string |||
626+
| label-width | width of label, e.g. '50px'. Width `auto` is supported. | string |||
627627
| required | whether the field is required or not, will be determined by validation rules if omitted | boolean || false |
628628
| rules | validation rules of form | object |||
629629
| error | field error message, set its value and the field will validate error and show this message immediately | string |||

examples/docs/es/form.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -607,7 +607,7 @@ Todos los componentes de un formulario heredan su atributo `size`. De manera sim
607607
| rules | Reglas de validación | object |||
608608
| inline | Si el form es inline | boolean || false |
609609
| label-position | Posicion de la etiqueta | string | left / right / top | right |
610-
| label-width | ancho de la etiqueta, y todos los form items directos descendientes heredarán este valor | string | | |
610+
| label-width | width of label, e.g. '50px'. All its direct child form items will inherit this value. Width `auto` is supported. | string || |
611611
| label-suffix | sufijo de la etiqueta | string |||
612612
| hide-required-asterisk | si los campos obligatorios deben tener un asterisco rojo (estrella) al lado de sus etiquetas | boolean || false |
613613
| show-message | si mostrar o no el mensaje de error | boolean || true |
@@ -638,7 +638,7 @@ Todos los componentes de un formulario heredan su atributo `size`. De manera sim
638638
| -------------- | ------------------------------------------------------------ | ------- | ------------------------------------------- | ----------- |
639639
| prop | un clave del modelo. En el uso del método validate and resetFields, el atributo es obligatorio. | string | Clave del modelo que se ha pasado a `form` | |
640640
| label | etiqueta | string |||
641-
| label-width | ancho de la etiqueta, e.g. '50px' | string |||
641+
| label-width | ancho de la etiqueta, e.g. '50px'. Width `auto` is supported | string |||
642642
| required | si el campo es obligatorio o no, estará determinado por las reglas de validación si se omite. | boolean || false |
643643
| rules | reglas de validacion del form | object |||
644644
| error | mensaje de error de campo, establezca su valor y el campo validará el error y mostrará este mensaje inmediatamente. | string |||

examples/docs/fr-FR/form.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -592,7 +592,7 @@ Tout les composants d'un formulaire héritent leur attribut `size` de ce formula
592592
| rules | Règles de validation du formulaire. | object |||
593593
| inline | Si le formulaire est horizontal. | boolean || false |
594594
| label-position | Position des labels. Si 'left' ou 'right', `label-width` est aussi requis. | string | left / right / top | right |
595-
| label-width | Largeur des labels, tout les enfants directs hériteront de cette valeur. | string |||
595+
| label-width | Largeur des labels, tout les enfants directs hériteront de cette valeur. Width `auto` is supported. | string |||
596596
| label-suffix | Suffixe de labels. | string |||
597597
| hide-required-asterisk | Si les champs obligatoires doivent avoir une astérisque rouge (étoile) à coté de leurs labels. | boolean || false |
598598
| show-message | Si le message d'erreur doit apparaître. | boolean || true |
@@ -623,7 +623,7 @@ Tout les composants d'un formulaire héritent leur attribut `size` de ce formula
623623
| ---- | ----| ---- | ---- | ---- |
624624
| prop | Une des clés de `model`. Utilisés par les méthodes validate et resetFields. Requis. | string | Clés du model passé à `form`. |
625625
| label | Le label. | string |||
626-
| label-width | Largeur du label, e.g. '50px'. | string |||
626+
| label-width | Largeur du label, e.g. '50px'. Width `auto` is supported. | string |||
627627
| required | Si le champ est requis ou non. Si omis, sera déterminé par les règles de validation. | boolean || false |
628628
| rules | Règles de validation du formulaire. | object |||
629629
| error | Message d'erreur du champ. Si il est modifié, le champ l'affichera immédiatement. | string |||

examples/docs/zh-CN/form.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -588,7 +588,7 @@ W3C 标准中有如下[规定](https://www.w3.org/MarkUp/html-spec/html-spec_8.h
588588
| rules | 表单验证规则 | object |||
589589
| inline | 行内表单模式 | boolean || false |
590590
| label-position | 表单域标签的位置,如果值为 left 或者 right 时,则需要设置 `label-width` | string | right/left/top | right |
591-
| label-width | 表单域标签的宽度,作为 Form 直接子元素的 form-item 会继承该值 | string |||
591+
| label-width | 表单域标签的宽度,例如 '50px'。作为 Form 直接子元素的 form-item 会继承该值。支持 `auto` | string |||
592592
| label-suffix | 表单域标签的后缀 | string |||
593593
| hide-required-asterisk | 是否显示必填字段的标签旁边的红色星号 | boolean || false |
594594
| show-message | 是否显示校验错误信息 | boolean || true |
@@ -618,7 +618,7 @@ W3C 标准中有如下[规定](https://www.w3.org/MarkUp/html-spec/html-spec_8.h
618618
|---------- |-------------- |---------- |-------------------------------- |-------- |
619619
| prop | 表单域 model 字段,在使用 validate、resetFields 方法的情况下,该属性是必填的 | string | 传入 Form 组件的 `model` 中的字段 ||
620620
| label | 标签文本 | string |||
621-
| label-width | 表单域标签的的宽度,例如 '50px' | string |||
621+
| label-width | 表单域标签的的宽度,例如 '50px'。支持 `auto` | string |||
622622
| required | 是否必填,如不设置,则会根据校验规则自动生成 | boolean || false |
623623
| rules | 表单验证规则 | object |||
624624
| error | 表单域验证错误信息, 设置该值会使表单验证状态变为`error`,并显示该错误信息 | string |||

packages/form/src/form-item.vue

+24-6
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,13 @@
99
},
1010
sizeClass ? 'el-form-item--' + sizeClass : ''
1111
]">
12-
<label :for="labelFor" class="el-form-item__label" :style="labelStyle" v-if="label || $slots.label">
13-
<slot name="label">{{label + form.labelSuffix}}</slot>
14-
</label>
12+
<label-wrap
13+
:is-auto-width="labelStyle && labelStyle.width === 'auto'"
14+
:update-all="form.labelWidth === 'auto'">
15+
<label :for="labelFor" class="el-form-item__label" :style="labelStyle" v-if="label || $slots.label">
16+
<slot name="label">{{label + form.labelSuffix}}</slot>
17+
</label>
18+
</label-wrap>
1519
<div class="el-form-item__content" :style="contentStyle">
1620
<slot></slot>
1721
<transition name="el-zoom-in-top">
@@ -39,7 +43,7 @@
3943
import emitter from 'element-ui/src/mixins/emitter';
4044
import objectAssign from 'element-ui/src/utils/merge';
4145
import { noop, getPropByPath } from 'element-ui/src/utils/util';
42-
46+
import LabelWrap from './label-wrap';
4347
export default {
4448
name: 'ElFormItem',
4549
@@ -77,6 +81,10 @@
7781
},
7882
size: String
7983
},
84+
components: {
85+
// use this component to calculate auto width
86+
LabelWrap
87+
},
8088
watch: {
8189
error: {
8290
immediate: true,
@@ -108,7 +116,13 @@
108116
if (this.form.labelPosition === 'top' || this.form.inline) return ret;
109117
if (!label && !this.labelWidth && this.isNested) return ret;
110118
const labelWidth = this.labelWidth || this.form.labelWidth;
111-
if (labelWidth) {
119+
if (labelWidth === 'auto') {
120+
if (this.labelWidth === 'auto') {
121+
ret.marginLeft = this.computedLabelWidth;
122+
} else if (this.form.labelWidth === 'auto') {
123+
ret.marginLeft = this.elForm.autoLabelWidth;
124+
}
125+
} else {
112126
ret.marginLeft = labelWidth;
113127
}
114128
return ret;
@@ -167,7 +181,8 @@
167181
validateMessage: '',
168182
validateDisabled: false,
169183
validator: {},
170-
isNested: false
184+
isNested: false,
185+
computedLabelWidth: ''
171186
};
172187
},
173188
methods: {
@@ -261,6 +276,9 @@
261276
}
262277
263278
this.validate('change');
279+
},
280+
updateComputedLabelWidth(width) {
281+
this.computedLabelWidth = width ? `${width}px` : '';
264282
}
265283
},
266284
mounted() {

packages/form/src/form.vue

+28-1
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,16 @@
5454
}
5555
}
5656
},
57+
computed: {
58+
autoLabelWidth() {
59+
const max = Math.max(...this.potentialLabelWidthArr);
60+
return max ? `${max}px` : '';
61+
}
62+
},
5763
data() {
5864
return {
59-
fields: []
65+
fields: [],
66+
potentialLabelWidthArr: [] // use this array to calculate auto width
6067
};
6168
},
6269
created() {
@@ -142,6 +149,26 @@
142149
fields.forEach(field => {
143150
field.validate('', cb);
144151
});
152+
},
153+
getLabelWidthIndex(width) {
154+
const index = this.potentialLabelWidthArr.indexOf(width);
155+
// it's impossible
156+
if (index === -1) {
157+
throw new Error('[ElementForm]unpected width ', width);
158+
}
159+
return index;
160+
},
161+
registerLabelWidth(val, oldVal) {
162+
if (val && oldVal) {
163+
const index = this.getLabelWidthIndex(oldVal);
164+
this.potentialLabelWidthArr.splice(index, 1, val);
165+
} else if (val) {
166+
this.potentialLabelWidthArr.push(val);
167+
}
168+
},
169+
deregisterLabelWidth(val) {
170+
const index = this.getLabelWidthIndex(val);
171+
this.potentialLabelWidthArr.splice(index, 1);
145172
}
146173
}
147174
};

packages/form/src/label-wrap.vue

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
<script>
2+
3+
export default {
4+
props: {
5+
isAutoWidth: Boolean,
6+
updateAll: Boolean
7+
},
8+
9+
inject: ['elForm', 'elFormItem'],
10+
11+
render() {
12+
const slots = this.$slots.default;
13+
if (!slots) return null;
14+
if (this.isAutoWidth) {
15+
return (<div class="el-form-item__label-wrap">
16+
{ slots }
17+
</div>);
18+
} else {
19+
return slots[0];
20+
}
21+
},
22+
23+
methods: {
24+
getLabelWidth() {
25+
if (this.$el && this.$el.firstElementChild) {
26+
const computedWidth = window.getComputedStyle(this.$el.firstElementChild).width;
27+
return Math.ceil(parseFloat(computedWidth));
28+
} else {
29+
return 0;
30+
}
31+
},
32+
updateLabelWidth(action = 'update') {
33+
if (this.$slots.default && this.isAutoWidth && this.$el.firstElementChild) {
34+
if (action === 'update') {
35+
this.computedWidth = this.getLabelWidth();
36+
} else if (action === 'remove') {
37+
this.elForm.deregisterLabelWidth(this.computedWidth);
38+
}
39+
}
40+
}
41+
},
42+
43+
watch: {
44+
computedWidth(val, oldVal) {
45+
if (this.updateAll) {
46+
this.elForm.registerLabelWidth(val, oldVal);
47+
this.elFormItem.updateComputedLabelWidth(val);
48+
}
49+
}
50+
},
51+
52+
data() {
53+
return {
54+
computedWidth: 0
55+
};
56+
},
57+
58+
mounted() {
59+
this.updateLabelWidth('update');
60+
},
61+
62+
// Is this necessary?
63+
// updated() {
64+
// this.updateLabelWidth('update');
65+
// },
66+
67+
beforeDestroy() {
68+
this.updateLabelWidth('remove');
69+
}
70+
};
71+
</script>

packages/theme-chalk/src/form.scss

+8
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,14 @@
8484
}
8585
}
8686

87+
@include e(label-wrap) {
88+
float: left;
89+
.el-form-item__label {
90+
display: inline-block;
91+
float: none;
92+
}
93+
}
94+
8795
@include e(label) {
8896
text-align: right;
8997
vertical-align: middle;

test/unit/specs/form.spec.js

+38-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { createVue, destroyVM } from '../util';
1+
import { createVue, destroyVM, waitImmediate } from '../util';
22

33
const DELAY = 50;
44

@@ -43,6 +43,43 @@ describe('Form', () => {
4343
expect(vm.$el.querySelector('.el-form-item__content').style.marginLeft).to.equal('80px');
4444
done();
4545
});
46+
it('auto label width', async() => {
47+
vm = createVue({
48+
template: `
49+
<el-form ref="form" :model="form" label-width="auto">
50+
<el-form-item label="活动名称">
51+
<el-input v-model="form.name"></el-input>
52+
</el-form-item>
53+
<el-form-item label="活动备注信息" v-if="display">
54+
<el-input v-model="form.name"></el-input>
55+
</el-form-item>
56+
</el-form>
57+
`,
58+
data() {
59+
return {
60+
display: true,
61+
form: {
62+
name: '',
63+
intro: ''
64+
}
65+
};
66+
}
67+
}, true);
68+
69+
await waitImmediate();
70+
71+
const formItems = vm.$el.querySelectorAll('.el-form-item__content');
72+
const marginLeft = parseInt(formItems[0].style.marginLeft, 10);
73+
const marginLeft1 = parseInt(formItems[1].style.marginLeft, 10);
74+
expect(marginLeft === marginLeft1).to.be.true;
75+
76+
vm.display = false;
77+
await waitImmediate();
78+
79+
const formItem = vm.$el.querySelector('.el-form-item__content');
80+
const newMarginLeft = parseInt(formItem.style.marginLeft, 10);
81+
expect(newMarginLeft < marginLeft).to.be.true;
82+
});
4683
it('inline form', done => {
4784
vm = createVue({
4885
template: `

0 commit comments

Comments
 (0)