一. Checkbox 组件提供的属性和事件
1.1 属性
以下是 Checkbox
组件提供的属性:
参数 | 说明 | 类型 | 可选值 | 默认值 |
---|---|---|---|---|
value / v-model | 绑定值 | string / number / boolean | -- | -- |
label | 选中状态的值(只有在checkbox-group 或者绑定对象类型为array 时有效) |
string / number / boolean | -- | -- |
true-label | 选中时的值 | string / number | -- | -- |
false-label | 没有选中时的值 | string / number | -- | -- |
disabled | 是否禁用 | boolean | -- | false |
border | 是否显示边框 | boolean | -- | false |
size | Checkbox 的尺寸,仅在 border 为真时有效 | string | medium / small / mini | -- |
name | 原生 name 属性 | string | -- | -- |
checked | 当前是否勾选 | boolean | -- | false |
indeterminate | 设置 indeterminate 状态,只负责样式控制 | boolean | -- | false |
1.2 事件
事件名称 | 说明 | 回调参数 |
---|---|---|
change | 当绑定值变化时触发的事件 | 更新后的值 |
二. 按照步骤去实现 Checkbox 组件的属性和事件
- 注册组件。
checkbox
的基本template
部分。- 设置
checkbox
的中文描述。 - 设置
checkbox
的绑定值value/v-model
。 true-label
和false-label
属性的处理。- 当前是否勾选
checked
属性的处理。 - 禁用
disabled
属性的处理。 - 边框
border
属性的处理。 - 尺寸
size
属性的处理。 - 半选状态
indeterminate
属性的处理。 - 原生
name
属性的处理。 - 屏幕阅读器相关处理。
focus
状态的处理。
三. Checkbox 组件属性和事件的具体实现
3.1 注册组件
新建一个 checkbox.vue
文件,设置组件的 name
和 componentName
都为 ElCheckbox
。
在 main.js
文件里面引入这个组件,并且使用 Vue.component
来全局注册这个组件。
3.2 checkbox 的基本 template 部分
以下是 checkbox
组件基础的 HTML 结构部分:
html
<template>
<!-- checkbox 整体用 label 标签包裹 -->
<label
class="el-checkbox"
>
<!-- checkbox 左侧的点击按钮,用 span 做样式,将 input type=checkbox 放置在 span 标签的下方 -->
<span class="el-checkbox__input">
<span class="el-checkbox__inner"></span>
<input
class="el-checkbox__original"
type="checkbox"
/>
</span>
<!-- checkbox 右侧的描述 -->
<span class="el-checkbox__label"></span>
</label>
</template>
<script>
export default {
name: 'ElCheckbox',
componentName: 'ElCheckbox',
}
</script>
3.3 设置 checkbox 的中文描述
实现步骤:
- 新建一个文件,文件内部引入
checkbox
组件,并且在组件内部写一段中文描述。
html
<template>
<div>
<el-checkbox>备选项</el-checkbox>
</div>
</template>
<script>
export default {
}
</script>
- 在组件内部的右侧描述中使用
slot
插槽的形式去接收父组件传过来标签内内容。
html
<!-- checkbox 右侧的描述 -->
<span class="el-checkbox__label">
<slot></slot>
</span>
3.4 设置 checkbox 的绑定值 value/v-model
3.4.1 v-model 绑定 Boolean 类型的值
实现步骤:
- 使用 checkbox 组件时,用
v-model
绑定一个 Boolean 类型的值,初始值为false
。
html
<template>
<div>
<el-checkbox v-model="isCheck">备选项</el-checkbox>
</div>
</template>
<script>
export default {
data() {
return {
isCheck: false
}
}
}
</script>
- 组件内部默认用
value
去接收。
js
props: {
value: {},
}
- data 中定义
selfModel
变量,作为记录组件内部值的变量,该变量默认值为false
。
js
data() {
return {
selfModel: false,
}
},
- 定义计算属性
model
作为组件内部input
的v-model
绑定值。
(1)在model
的getter
方法里面判断传过来的value
是否为undefined
,如果不是undefined
则采用传过来的value
值,否则采用selfModel
变量的默认false
的值。
js
computed: {
model: {
get() {
return this.value !== undefined ? this.value : this.selfModel;
},
...
}
},
(2)在 model
的 setter
方法里面当值变化时,调用父组件的 input
方法,并且将 selfModel
变量的值设置为改变后的值。
js
computed: {
model: {
...
set(val) {
this.$emit('input', val);
this.selfModel = val;
}
}
},
(3)将组件内部 input
的 v-model
值绑定为 model
变量。
html
<span class="el-checkbox__input">
<span class="el-checkbox__inner"></span>
<!-- 将 v-model 的值绑定为 model 变量 -->
<input
class="el-checkbox__original"
type="checkbox"
v-model="model"
/>
</span>
(4)设置组件的选中:设置计算属性 isChecked
判断 model
为 Boolean 类型时的选中结果,并且将 is-checked
的样式添加到 dom 元素上。
js
computed: {
isChecked() {
if ({}.toString.call(this.model) === '[object Boolean]') {
return this.model;
}
}
}
(5)切换选中时让父组件可以监听到 change
事件:input
增加 change
事件监听方法 handleChange
,handleChange
方法中使用 emit
让父组件监听到 change
事件。
html
<input
class="el-checkbox__original"
type="checkbox"
:value="label"
v-model="model"
@change="handleChange"
/>
js
methods: {
handleChange(ev) {
let value = ev.target.checked;
this.$emit('change', value, ev);
}
}
3.4.2 v-model 绑定 Array 类型的值
v-model
也可以绑定 Array
类型的值,此时需要 checkbox
组件传入 label
属性。
实现步骤:
- 使用
checkbox
组件时,传入label
属性,并且将v-model
的绑定值改为数组类型。
html
<template>
<div>
<el-checkbox v-model="checkList" :label="labelVal">备选项</el-checkbox>
</div>
</template>
<script>
export default {
data() {
return {
labelVal: 1,
checkList: [1]
}
}
}
</script>
- 使用
props
接收传入的label
属性。
js
props: {
value: {},
}
- 将组件内部
input
的value
值绑定为传入的label
。
html
<input
class="el-checkbox__original"
type="checkbox"
:value="label"
v-model="model"
/>
- 完善组件的右侧描述部分,如果父组件标签内未传入内容,但是
label
属性有值,则展示label
属性的值。
html
<!-- checkbox 右侧的描述 -->
<span class="el-checkbox__label" v-if="$slots.default || label">
<slot></slot>
<template v-if="!$slots.default">{{label}}</template>
</span>
- 修改计算属性
isChecked
,支持传入数组的选中。
js
isChecked() {
...
} else if (Array.isArray(this.model)) {
return this.model.indexOf(this.label) > -1
}
}
3.5 true-label 和 false-label 属性的处理
true-label 和 false-label 介绍:
true-label
和false-label
是 Vue 特有的属性,需要和v-model
配合起来使用。true-label
代表选中时被设置的值,false-label
代表未被选中时被设置的值。
实现步骤:
- 父组件传入
true-label
和false-label
属性,并且将v-model
改为与true-label
一致,将组件内部传入的文案也设置为v-model
绑定的变量。
html
<template>
<div>
<el-checkbox
v-model="checkVal"
true-label="选中了"
false-label="未选中"
>{{checkVal}}</el-checkbox>
</div>
</template>
<script>
export default {
data() {
return {
checkVal: "选中了"
}
},
}
</script>
- props 接收传入的
true-label
和false-label
属性。
js
props: {
trueLabel: [String, Number],
falseLabel: [String, Number],
}
- 修改
input
,根据是否传入true-label
或false-label
属性,来展示不同的input
。判断如果传入了true-label
或false-label
,则将value
属性去掉,增加true-value
和false-value
属性。
html
<input
v-if="trueLabel || falseLabel"
class="el-checkbox__original"
type="checkbox"
:true-value="trueLabel"
:false-value="falseLabel"
v-model="model"
@change="handleChange"
/>
<input
v-else
class="el-checkbox__original"
type="checkbox"
:value="label"
v-model="model"
@change="handleChange"
/>
- 完善计算属性
isChecked
,如果传入的不是布尔类型,也不是数组,则判断是否和true-label
的值一致,如果一致也为选中状态。
js
computed: {
isChecked() {
...
} else if (this.model !== null && this.model !== undefined) {
return this.model === this.trueLabel;
}
}
}
- 修改
handleChange
方法,如果传入了true-label
或false-label
属性,则将返回的value
值改为true-label
或false-label
属性的值。
js
methods: {
...
handleChange(ev) {
let value;
if (ev.target.checked) {
value = this.trueLabel === undefined ? true : this.trueLabel;
} else {
value = this.falseLabel === undefined ? false : this.falseLabel;
}
this.$emit('change', value, ev);
}
}
3.6 当前是否勾选 checked 属性的处理
实现步骤:
- 父组件传入
checked
属性。
html
<template>
<div>
<el-checkbox
v-model="isChecked"
checked
>备选项</el-checkbox>
</div>
</template>
<script>
export default {
data() {
return {
isChecked: false
}
},
}
</script>
props
监听父组件传过来的checked
属性。
js
props: {
...
checked: Boolean,
...
}
- 如果
checked
为true
,则改变计算属性model
的值,如果model
是数组且不包含checked
为true
的选项,则将该选项push
进model
数组中,如果不是数组则将model
优先设置为true-label
的值,如果true-label
没有值,则设置为true
。
js
methods: {
addToStore() {
if (
Array.isArray(this.model) &&
this.model.indexOf(this.label) === -1
) {
this.model.push(this.label);
} else {
this.model = this.trueLabel || true;
}
},
},
created() {
this.checked && this.addToStore();
}
3.7 禁用 disabled 属性的处理
实现步骤:
- 父组件传入
disabled
属性。
html
<template>
<div>
<el-checkbox
v-model="isChecked"
disabled
>备选项</el-checkbox>
</div>
</template>
<script>
export default {
data() {
return {
isChecked: false
}
},
}
</script>
props
监听父组件传过来的disabled
属性。
js
props: {
...
disabled: Boolean,
...
}
- 计算属性
isDisabled
监听disabled
的改变。
js
computed: {
...
isDisabled() {
return this.disabled;
}
...
}
- 给
input
元素增加disabled
属性:disabled="isDisabled"
。
html
<input
v-if="trueLabel || falseLabel"
class="el-checkbox__original"
type="checkbox"
:disabled="isDisabled"
:true-value="trueLabel"
:false-value="falseLabel"
v-model="model"
@change="handleChange"
/>
<input
v-else
class="el-checkbox__original"
type="checkbox"
:disabled="isDisabled"
:value="label"
v-model="model"
@change="handleChange"
/>
- 在
el-checkbox
和el-checkbox__input
增加disabled
属性的样式。
html
<label
class="el-checkbox"
:class="[
...
{ 'is-checked': isChecked }
]"
>
<!-- checkbox 左侧的点击按钮,用 span 做样式,将 input type=checkbox 放置在 span 标签的下方 -->
<span
class="el-checkbox__input"
:class="{
...
'is-checked': isChecked,
}"
>
...
</span>
...
</label>
3.8 边框 border 属性的处理
实现步骤:
- 父组件传入
border
属性。
html
<template>
<div>
<!-- 传入 border 属性 -->
<el-checkbox
v-model="isChecked"
border
>备选项</el-checkbox>
</div>
</template>
<script>
export default {
data() {
return {
isChecked: false
}
},
}
</script>
props
监听父组件传入的border
属性。
js
props: {
border: Boolean,
}
- 在
el-checkbox
上面增加border
属性的样式。
html
<label
class="el-checkbox"
:class="[
...
{ 'is-bordered': border },
]"
>
...
</label>
3.9 尺寸 size 属性的处理
实现步骤:
- 父组件传入
size
属性。
html
<template>
<div>
<!-- 传入 size="mini" -->
<el-checkbox
v-model="isChecked"
border
size="mini"
>备选项</el-checkbox>
</div>
</template>
<script>
export default {
data() {
return {
isChecked: false
}
},
}
</script>
props
监听传入的size
属性。
js
props: {
size: String
}
- 设置计算属性
checkboxSize
监听传入的size
属性的改变。
js
computed: {
checkboxSize() {
return this.size;
}
}
- 当
border
和size
两个属性均存在的情况下,将size
的样式加在el-checkbox
元素上。
html
<label
class="el-checkbox"
:class="[
border && checkboxSize ? 'el-checkbox--' + checkboxSize : '',
...
]"
>
...
</label>
3.10 半选状态 indeterminate 属性的处理
半选 indeterminate
是指中间有一个横线的半选状态样式,多用于其下级有选中和未选中两种,则父级展示半选状态。
实现步骤:
- 父组件传入
indeterminate
属性。
html
<template>
<div>
<el-checkbox
v-model="isChecked"
:indeterminate="isIndeterminate"
>备选项</el-checkbox>
</div>
</template>
<script>
export default {
data() {
return {
isChecked: false,
isIndeterminate: true
}
},
}
</script>
props
监听父组件传入的indeterminate
属性。
js
props: {
...
indeterminate: Boolean,
...
}
- 在
el-checkbox__input
上面增加indeterminate
样式。
html
<!-- checkbox 左侧的点击按钮,用 span 做样式,将 input type=checkbox 放置在 span 标签的下方 -->
<span
class="el-checkbox__input"
:class="{
...
'is-indeterminate': indeterminate,
}"
>
...
</span>
3.11 原生 name 属性的处理
实现步骤:
- 监听父组件传过来的
name
属性。
js
props: {
name: String
}
- 将
name
属性绑定在checkbox
上。
html
<input
v-if="trueLabel || falseLabel"
class="el-checkbox__original"
type="checkbox"
:name="name"
:disabled="isDisabled"
:true-value="trueLabel"
:false-value="falseLabel"
v-model="model"
@change="handleChange"
/>
<input
v-else
class="el-checkbox__original"
type="checkbox"
:name="name"
:disabled="isDisabled"
:value="label"
v-model="model"
@change="handleChange"
/>
3.12 屏幕阅读器相关处理
aria-controls 属性介绍:
aria-controls
是一种用于描述HTML元素之间关系的属性,它用来指定一个元素与另一个元素之间的关系。使用aria-controls
属性时,必须指定需要控制的元素的 ID,而且这个 ID 必须存在于页面中。
例如:在 checkbox
组件的半选按钮上面增加 aria-controls
属性,控制的其他按钮的范围外层增加 id
属性。
实现步骤:
props
接收id
和controls
属性,用于增加aria-controls
属性的值。
js
props: {
...
id: String,
controls: String,
...
}
- 将传入的
id
属性增加到组件根元素上面。
html
<label
class="el-checkbox"
...
:id="id"
>
</label>
- 给组件根元素增加
aria-controls
属性。
js
mounted() {
if (this.indeterminate) {
this.$el.setAttribute('aria-controls', this.controls);
}
},
- 增加
role
属性、aria-
前缀属性、tabIndex
属性。
(1)给最外层 label
元素增加如下属性,表示如果是半选则增加 role
、tabIndex
和 aria-checked
属性。
html
<label
class="el-checkbox"
...
:tabindex="indeterminate ? 0 : false"
:role="indeterminate ? 'checkbox' : false"
:aria-checked="indeterminate ? 'mixed' : false"
>
</label>
(2)给内层的 input
元素增加属性,表明如果是半选,对屏幕阅读器隐藏 input
。
html
<input
v-if="trueLabel || falseLabel"
class="el-checkbox__original"
...
:aria-hidden="indeterminate ? 'true' : 'false'"
/>
<input
v-else
class="el-checkbox__original"
...
:aria-hidden="indeterminate ? 'true' : 'false'"
...
/>
注意:
-
和源代码不同的地方是,源代码中将
role
等属性加到了包裹input
的span
标签上,但是经查阅aria-controls
属性,使用该属性的元素必须具有与其相应的角色与状态属性,则将role
等属性放到了最外层的label
标签上面。 -
controls
属性和id
属性在Element-ui
的checkbox
组件文档中并没有写明这两个属性,是在源代码中发现的可以支持这两个属性。
3.13 focus 状态的处理
增加 is-focus
的 class
,用于样式处理。
实现步骤:
- 在
data
下设置focus
变量,初始值为false
。
js
data() {
return {
...
focus: false,
}
},
- 在触发
input
的focus
和blur
时,切换focus
变量。
html
<input
...
@focus="focus = true"
@blur="focus = false"
/>
<input
...
@focus="focus = true"
@blur="focus = false"
/>
在 el-checkbox__input
增加 focus
样式。
html
<!-- checkbox 左侧的点击按钮,用 span 做样式,将 input type=checkbox 放置在 span 标签的下方 -->
<span
class="el-checkbox__input"
:class="{
...
'is-focus': focus
}"
>
...
</span>
相关 focus
样式,是在 focus
时出现的蓝色边框。
css
.el-checkbox__inner {
border-color: $--checkbox-input-border-color-hover;
}
四. 整体代码详解
包含 Checkbox
与 Checkbox-group
组件以及 Form
组件相互作用部分。
html
<template>
<!-- checkbox 整体用 label 标签包裹 -->
<label
class="el-checkbox"
:class="[
border && checkboxSize ? 'el-checkbox--' + checkboxSize : '',
{ 'is-disabled': isDisabled },
{ 'is-bordered': border },
{ 'is-checked': isChecked }
]"
:id="id"
:tabindex="indeterminate ? 0 : false"
:role="indeterminate ? 'checkbox' : false"
:aria-checked="indeterminate ? 'mixed' : false"
>
<!-- checkbox 左侧的点击按钮,用 span 做样式,将 input type=checkbox 放置在 span 标签的下方 -->
<span
class="el-checkbox__input"
:class="{
'is-disabled': isDisabled,
'is-checked': isChecked,
'is-indeterminate': indeterminate,
'is-focus': focus
}"
>
<span class="el-checkbox__inner"></span>
<input
v-if="trueLabel || falseLabel"
class="el-checkbox__original"
type="checkbox"
:aria-hidden="indeterminate ? 'true' : 'false'"
:name="name"
:disabled="isDisabled"
:true-value="trueLabel"
:false-value="falseLabel"
v-model="model"
@change="handleChange"
@focus="focus = true"
@blur="focus = false"
/>
<input
v-else
class="el-checkbox__original"
type="checkbox"
:aria-hidden="indeterminate ? 'true' : 'false'"
:name="name"
:disabled="isDisabled"
:value="label"
v-model="model"
@change="handleChange"
@focus="focus = true"
@blur="focus = false"
/>
</span>
<!-- checkbox 右侧的描述 -->
<span class="el-checkbox__label" v-if="$slots.default || label">
<slot></slot>
<template v-if="!$slots.default">{{label}}</template>
</span>
</label>
</template>
<script>
import Emitter from '../../utils/mixins/emitter';
export default {
name: 'ElCheckbox',
mixins: [Emitter],
inject: {
elForm: {
default: ''
},
elFormItem: {
default: ''
}
},
componentName: 'ElCheckbox',
data() {
return {
selfModel: false, // 组件内部的值
focus: false, // focus 状态
isLimitExceeded: false // 是否超出可选数量限制
}
},
computed: {
model: {
get() {
// 判断是否是按钮组,如果是按钮组,则获取按钮组的值,否则获取 value,如果 value 是 undefined,则获取组件内部的 selfModel 值
return this.isGroup
? this.store : this.value !== undefined
? this.value : this.selfModel;
},
set(val) {
// 先来判断是否是按钮组
if (this.isGroup) {
// 是按钮组的情况下
// 如果当前选中的数量小于最小的限制数量,则将 isLimitExceeded 设置为 true,表示已选数量与限制数量不符
(this._checkboxGroup.min !== undefined &&
val.length < this._checkboxGroup.min &&
(this.isLimitExceeded = true));
// 如果当前选中的数量大于最大的限制数量,则将 isLimitExceeded 设置为 true,表示已选数量与限制数量不符
(this._checkboxGroup.max !== undefined &&
val.length > this._checkboxGroup.max &&
(this.isLimitExceeded = true));
// 如果 isLimitExceeded 为 false,则出发 checkbox-grouo 的 input 事件
this.isLimitExceeded === false &&
this.dispatch('ElCheckboxGroup', 'input', [val]);
} else {
// 不是按钮组的情况,触发父组件的 input 事件,并且将组件内部的 selfModel 变量设置为当前的 value 值
this.$emit('input', val);
this.selfModel = val;
}
}
},
// 是否选中
isChecked() {
// 判断 model 值的类型,根据类型判断是否选中
if ({}.toString.call(this.model) === '[object model]') {
// 如果是布尔类型,则直接返回 model 的值用来判断是否选中
return this.model;
} else if (Array.isArray(this.model)) {
// 如果是数组,则判断当前 model 数组是否包含当前 label 的值,如果包含,则选中
return this.model.indexOf(this.label) > -1
} else if (this.model !== null && this.model !== undefined) {
// 如果不是布尔类型,也不是数组类型,且 model 不为 null 和 undefined
// 则判断 model 与 trueLabel(trueLabel是选中时显示的值)是否相等,如果相等,则选中
return this.model === this.trueLabel;
}
},
// 判断是否被包含在了按钮组里面,如果外层有 checkbox-group 组件,则说明被包含在按钮组里面
isGroup() {
let parent = this.$parent;
while (parent) {
if (parent.$options.componentName !== 'ElCheckboxGroup') {
parent = parent.$parent;
} else {
this._checkboxGroup = parent;
return true;
}
}
return false;
},
store() {
// 如果是按钮组,则返回按钮组的 value,否则返回组件的 value
return this._checkboxGroup ? this._checkboxGroup.value : this.value;
},
// 判断是否由于不符合可选数量造成禁用
isLimitDisabled() {
const { max, min } = this._checkboxGroup;
// 当已选数量大于等于最大限制数量时,禁用按钮组中没选中的 checkbox
// 当已选数量小于等于最小限制数量时,禁用按钮组中已选中的 checkbox
return !!(max || min) &&
(this.model.length >= max && !this.isChecked) ||
(this.model.length <= min && this.isChecked);
},
// disabled 计算
isDisabled() {
// 复选框组优先级:group > 组件内部disabled > Form > 可选数量限制disabled
// 复选框优先级:组件内部disabled > Form
return this.isGroup
? this._checkboxGroup.disabled || this.disabled || (this.elForm || {}).disabled || this.isLimitDisabled
: this.disabled || (this.elForm || {}).disabled;
},
// 获取 Form-Item 组件的 elFormItemSize 变量
_elFormItemSize() {
return (this.elFormItem || {}).elFormItemSize;
},
// Checkbox 的尺寸计算
checkboxSize() {
// 设置变量 temCheckboxSize,优先获取组件内部的 size,再获取 Form-Item 组件的 elFormItemSize
const temCheckboxSize = this.size || this._elFormItemSize
// 复选框组优先级:group-size > 组件内部size > Form-Item > Form
// 复选框优先级:组件内部size > Form-Item > Form
return this.isGroup
? this._checkboxGroup.checkboxGroupSize || temCheckboxSize
: temCheckboxSize;
}
},
props: {
value: {},
label: {},
indeterminate: Boolean,
disabled: Boolean,
checked: Boolean,
name: String,
trueLabel: [String, Number],
falseLabel: [String, Number],
id: String,
controls: String,
border: Boolean,
size: String
},
methods: {
// 如果最开始是选中状态,则将值存储到 model 变量中
addToStore() {
if (
Array.isArray(this.model) &&
this.model.indexOf(this.label) === -1
) {
// model 为数组,并且 model 不包含 label 的值,则将 label 的值 push 到 model 数组中
this.model.push(this.label);
} else {
// model 不是数组,如果有 true-label,则设置为 true-label 的值,否则设置为 true
this.model = this.trueLabel || true;
}
},
// 触发 change 事件
handleChange(ev) {
// 如果不符合可选的数量限制,则直接 return
if (this.isLimitExceeded) return;
let value;
if (ev.target.checked) {
// 如果选中了,存在 true-label 则设置为 true-label,否则设置为 true
value = this.trueLabel === undefined ? true : this.trueLabel;
} else {
// 如果没有选中,存在 false-label 则设置为 false-label,否则设置为 false
value = this.falseLabel === undefined ? false : this.falseLabel;
}
// 触发父组件的 change 事件
this.$emit('change', value, ev);
this.$nextTick(() => {
if (this.isGroup) {
// 如果被包含在按钮组里面,则触发按钮组的 change 事件
this.dispatch('ElCheckboxGroup', 'change', [this._checkboxGroup.value]);
}
});
}
},
created() {
// 如果最开始是选中状态,则将值存储到 model 变量中
this.checked && this.addToStore();
},
mounted() {
if (this.indeterminate) {
// 屏幕阅读器的处理,如果是半选状态,则增加 aria-controls 属性
this.$el.setAttribute('aria-controls', this.controls);
}
},
watch: {
value(value) {
// 监听 value 值的变化,当 value 值改变时,向 Form-Item 组件派发 el.form.change 自定义事件
this.dispatch('ElFormItem', 'el.form.change', value);
}
}
}
</script>