feat(directives): introduce created custom directive hook and ensure

`v-model` event listener fire before template/props listeners

fix #1931
This commit is contained in:
Evan You
2020-08-24 17:12:16 -04:00
parent 016ba116a8
commit 11804fe93f
4 changed files with 28 additions and 16 deletions

View File

@@ -46,7 +46,7 @@ type ModelDirective<T> = ObjectDirective<T & { _assign: AssignerFn }>
export const vModelText: ModelDirective<
HTMLInputElement | HTMLTextAreaElement
> = {
beforeMount(el, { value, modifiers: { lazy, trim, number } }, vnode) {
created(el, { value, modifiers: { lazy, trim, number } }, vnode) {
el.value = value == null ? '' : value
el._assign = getModelAssigner(vnode)
const castToNumber = number || el.type === 'number'
@@ -90,7 +90,7 @@ export const vModelText: ModelDirective<
}
export const vModelCheckbox: ModelDirective<HTMLInputElement> = {
beforeMount(el, binding, vnode) {
created(el, binding, vnode) {
setChecked(el, binding, vnode)
el._assign = getModelAssigner(vnode)
addEventListener(el, 'change', () => {
@@ -135,7 +135,7 @@ function setChecked(
}
export const vModelRadio: ModelDirective<HTMLInputElement> = {
beforeMount(el, { value }, vnode) {
created(el, { value }, vnode) {
el.checked = looseEqual(value, vnode.props!.value)
el._assign = getModelAssigner(vnode)
addEventListener(el, 'change', () => {
@@ -151,16 +151,19 @@ export const vModelRadio: ModelDirective<HTMLInputElement> = {
}
export const vModelSelect: ModelDirective<HTMLSelectElement> = {
// use mounted & updated because <select> relies on its children <option>s.
mounted(el, { value }, vnode) {
setSelected(el, value)
el._assign = getModelAssigner(vnode)
created(el, binding, vnode) {
addEventListener(el, 'change', () => {
const selectedVal = Array.prototype.filter
.call(el.options, (o: HTMLOptionElement) => o.selected)
.map(getValue)
el._assign(el.multiple ? selectedVal : selectedVal[0])
})
el._assign = getModelAssigner(vnode)
},
// set value in mounted & updated because <select> relies on its children
// <option>s.
mounted(el, { value }) {
setSelected(el, value)
},
beforeUpdate(el, _binding, vnode) {
el._assign = getModelAssigner(vnode)
@@ -214,8 +217,8 @@ function getCheckboxValue(
export const vModelDynamic: ObjectDirective<
HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement
> = {
beforeMount(el, binding, vnode) {
callModelHook(el, binding, vnode, null, 'beforeMount')
created(el, binding, vnode) {
callModelHook(el, binding, vnode, null, 'created')
},
mounted(el, binding, vnode) {
callModelHook(el, binding, vnode, null, 'mounted')
@@ -233,7 +236,7 @@ function callModelHook(
binding: DirectiveBinding,
vnode: VNode,
prevVNode: VNode | null,
hook: 'beforeMount' | 'mounted' | 'beforeUpdate' | 'updated'
hook: keyof ObjectDirective
) {
let modelToUse: ObjectDirective
switch (el.tagName) {
@@ -244,7 +247,7 @@ function callModelHook(
modelToUse = vModelText
break
default:
switch (el.type) {
switch (vnode.props && vnode.props.type) {
case 'checkbox':
modelToUse = vModelCheckbox
break