@@ -6,16 +6,18 @@ import {
66 looseIndexOf ,
77 looseToNumber ,
88} from '@vue/shared'
9- import type {
10- DirectiveBinding ,
11- DirectiveHook ,
12- DirectiveHookName ,
13- ObjectDirective ,
14- } from '../directives'
9+ import type { Directive } from '../directives'
1510import { addEventListener } from '../dom/event'
1611import { nextTick } from '../scheduler'
1712import { warn } from '../warning'
1813import { MetadataKind , getMetadata } from '../componentMetadata'
14+ import {
15+ onBeforeMount ,
16+ onBeforeUnmount ,
17+ onBeforeUpdate ,
18+ onMounted ,
19+ } from '../apiLifecycle'
20+ import { renderEffect } from '../renderEffect'
1921
2022type AssignerFn = ( value : any ) => void
2123function getModelAssigner ( el : Element ) : AssignerFn {
@@ -41,12 +43,12 @@ const assigningMap = new WeakMap<HTMLElement, boolean>()
4143
4244// We are exporting the v-model runtime directly as vnode hooks so that it can
4345// be tree-shaken in case v-model is never used.
44- export const vModelText : ObjectDirective <
46+ export const vModelText : Directive <
4547 HTMLInputElement | HTMLTextAreaElement ,
4648 any ,
4749 'lazy' | 'trim' | 'number'
48- > = {
49- beforeMount ( el , { modifiers : { lazy , trim , number } = { } } ) {
50+ > = ( { value : el } , { source , modifiers : { lazy , trim , number } = { } } ) => {
51+ onBeforeMount ( ( ) => {
5052 const assigner = getModelAssigner ( el )
5153 assignFnMap . set ( el , assigner )
5254
@@ -78,12 +80,15 @@ export const vModelText: ObjectDirective<
7880 // fires "change" instead of "input" on autocomplete.
7981 addEventListener ( el , 'change' , onCompositionEnd )
8082 }
81- } ,
82- // set value on mounted so it's after min/max for type="range"
83- mounted ( el , { value } ) {
83+ } )
84+
85+ onMounted ( ( ) => {
86+ const value = source ( )
8487 el . value = value == null ? '' : value
85- } ,
86- beforeUpdate ( el , { value, modifiers : { lazy, trim, number } = { } } ) {
88+ } )
89+
90+ renderEffect ( ( ) => {
91+ const value = source ( )
8792 assignFnMap . set ( el , getModelAssigner ( el ) )
8893
8994 // avoid clearing unresolved text. #2302
@@ -108,29 +113,34 @@ export const vModelText: ObjectDirective<
108113 }
109114
110115 el . value = newValue
111- } ,
116+ } )
112117}
113118
114- export const vModelRadio : ObjectDirective < HTMLInputElement > = {
115- beforeMount ( el , { value } ) {
116- el . checked = looseEqual ( value , getValue ( el ) )
119+ export const vModelRadio : Directive < HTMLInputElement > = (
120+ { value : el } ,
121+ { source } ,
122+ ) => {
123+ onBeforeMount ( ( ) => {
124+ el . checked = looseEqual ( source ( ) , getValue ( el ) )
117125 assignFnMap . set ( el , getModelAssigner ( el ) )
118126 addEventListener ( el , 'change' , ( ) => {
119127 assignFnMap . get ( el ) ! ( getValue ( el ) )
120128 } )
121- } ,
122- beforeUpdate ( el , { value, oldValue } ) {
129+ } )
130+
131+ renderEffect ( ( ) => {
132+ const value = source ( )
123133 assignFnMap . set ( el , getModelAssigner ( el ) )
124- if ( value !== oldValue ) {
125- el . checked = looseEqual ( value , getValue ( el ) )
126- }
127- } ,
134+ el . checked = looseEqual ( value , getValue ( el ) )
135+ } )
128136}
129137
130- export const vModelSelect : ObjectDirective < HTMLSelectElement , any , 'number' > = {
131- // <select multiple> value need to be deep traversed
132- deep : true ,
133- beforeMount ( el , { value, modifiers : { number = false } = { } } ) {
138+ export const vModelSelect : Directive < HTMLSelectElement , any , 'number' > = (
139+ { value : el } ,
140+ { source, modifiers : { number = false } = { } } ,
141+ ) => {
142+ onBeforeMount ( ( ) => {
143+ const value = source ( )
134144 const isSetModel = isSet ( value )
135145 addEventListener ( el , 'change' , ( ) => {
136146 const selectedVal = Array . prototype . filter
@@ -153,15 +163,17 @@ export const vModelSelect: ObjectDirective<HTMLSelectElement, any, 'number'> = {
153163 } )
154164 assignFnMap . set ( el , getModelAssigner ( el ) )
155165 setSelected ( el , value , number )
156- } ,
157- beforeUpdate ( el ) {
166+ } )
167+
168+ onBeforeUnmount ( ( ) => {
158169 assignFnMap . set ( el , getModelAssigner ( el ) )
159- } ,
160- updated ( el , { value, modifiers : { number = false } = { } } ) {
170+ } )
171+
172+ renderEffect ( ( ) => {
161173 if ( ! assigningMap . get ( el ) ) {
162- setSelected ( el , value , number )
174+ setSelected ( el , source ( ) , number )
163175 }
164- } ,
176+ } )
165177}
166178
167179function setSelected ( el : HTMLSelectElement , value : any , number : boolean ) {
@@ -223,27 +235,15 @@ function getCheckboxValue(el: HTMLInputElement, checked: boolean) {
223235 return checked
224236}
225237
226- const setChecked : DirectiveHook < HTMLInputElement > = (
227- el ,
228- { value , oldValue } ,
238+ export const vModelCheckbox : Directive < HTMLInputElement > = (
239+ { value : el } ,
240+ { source } ,
229241) => {
230- if ( isArray ( value ) ) {
231- el . checked = looseIndexOf ( value , getValue ( el ) ) > - 1
232- } else if ( isSet ( value ) ) {
233- el . checked = value . has ( getValue ( el ) )
234- } else if ( value !== oldValue ) {
235- el . checked = looseEqual ( value , getCheckboxValue ( el , true ) )
236- }
237- }
238-
239- export const vModelCheckbox : ObjectDirective < HTMLInputElement > = {
240- // #4096 array checkboxes need to be deep traversed
241- deep : true ,
242- beforeMount ( el , binding ) {
242+ onBeforeMount ( ( ) => {
243243 assignFnMap . set ( el , getModelAssigner ( el ) )
244244
245245 addEventListener ( el , 'change' , ( ) => {
246- const modelValue = binding . value
246+ const modelValue = source ( )
247247 const elementValue = getValue ( el )
248248 const checked = el . checked
249249 const assigner = assignFnMap . get ( el ) !
@@ -269,36 +269,38 @@ export const vModelCheckbox: ObjectDirective<HTMLInputElement> = {
269269 assigner ( getCheckboxValue ( el , checked ) )
270270 }
271271 } )
272- } ,
273- // set initial checked on mount to wait for true-value/false-value
274- mounted : setChecked ,
275- beforeUpdate ( el , binding ) {
272+ } )
273+
274+ onMounted ( ( ) => {
275+ setChecked ( )
276+ } )
277+
278+ onBeforeUpdate ( ( ) => {
276279 assignFnMap . set ( el , getModelAssigner ( el ) )
277- setChecked ( el , binding )
278- } ,
280+ setChecked ( )
281+ } )
282+
283+ function setChecked ( ) {
284+ const value = source ( )
285+ if ( isArray ( value ) ) {
286+ el . checked = looseIndexOf ( value , getValue ( el ) ) > - 1
287+ } else if ( isSet ( value ) ) {
288+ el . checked = value . has ( getValue ( el ) )
289+ } else {
290+ el . checked = looseEqual ( value , getCheckboxValue ( el , true ) )
291+ }
292+ }
279293}
280294
281- export const vModelDynamic : ObjectDirective <
295+ export const vModelDynamic : Directive <
282296 HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement
283- > = {
284- beforeMount ( el , binding ) {
285- callModelHook ( el , binding , 'beforeMount' )
286- } ,
287- mounted ( el , binding ) {
288- callModelHook ( el , binding , 'mounted' )
289- } ,
290- beforeUpdate ( el , binding ) {
291- callModelHook ( el , binding , 'beforeUpdate' )
292- } ,
293- updated ( el , binding ) {
294- callModelHook ( el , binding , 'updated' )
295- } ,
297+ > = ( elRef , binding ) => {
298+ const type = elRef . value . getAttribute ( 'type' )
299+ const modelToUse = resolveDynamicModel ( elRef . value . tagName , type )
300+ modelToUse ( elRef , binding )
296301}
297302
298- function resolveDynamicModel (
299- tagName : string ,
300- type : string | null ,
301- ) : ObjectDirective {
303+ function resolveDynamicModel ( tagName : string , type : string | null ) : Directive {
302304 switch ( tagName ) {
303305 case 'SELECT' :
304306 return vModelSelect
@@ -315,14 +317,3 @@ function resolveDynamicModel(
315317 }
316318 }
317319}
318-
319- function callModelHook (
320- el : HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement ,
321- binding : DirectiveBinding ,
322- hook : DirectiveHookName ,
323- ) {
324- const type = el . getAttribute ( 'type' )
325- const modelToUse = resolveDynamicModel ( el . tagName , type )
326- const fn = modelToUse [ hook ]
327- fn && fn ( el , binding )
328- }
0 commit comments