Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v-model changes #32

Closed
yyx990803 opened this issue Apr 9, 2019 · 3 comments
Closed

v-model changes #32

yyx990803 opened this issue Apr 9, 2019 · 3 comments

Comments

@yyx990803
Copy link
Member

yyx990803 commented Apr 9, 2019

Summary

Adjust v-model API when used on custom components for more flexible usage, and adjust compilation output on native elements for more succinct compiler output.

This builds on top of vuejs/rfcs#8 (Replace v-bind's .sync with a v-model argument).

Basic example

Motivation

Previously, v-model="foo" on components roughly compiles to the following:

h(Comp, {
  value: foo,
  onInput: value => {
    foo = value
  }
})

However, this requires the component to always use the value prop for binding with v-model when the component may want to expose the value prop for a different purpose.

In 2.2 we introduced the model component option that allows the component to customize the prop and event to use for v-model. However, this still only allows one v-model to be used on the component. In practice we are seeing some components that need to sync multiple values, and the other values have to use v-bind.sync. We noticed that v-model and v-bind.sync are fundamentally doing the same thing and can be combined into a single construct by allowing v-model to accept arguments (as proposed in vuejs/rfcs#8).

Detailed design

In 3.0, the model option will be removed. v-model="foo" (without argument) on a component compiles to the following instead:

h(Comp, {
  modelValue: foo,
  'onUpdate:modelValue': value => {
    foo = value
  }
})

If the component wants to support v-model without an argument, it should expect a prop named modelValue. To sync its value back to the parent, the child should emit an event named "update:modelValue" (see Render Function API change for details on the new VNode data structure).

The default compilation output uses the prop name modelValue so that it is clear this prop is compiled from v-model. This will be useful to differentiate it from the original value prop which could've been created manually by the user (especially in the native element case detailed in a later section).

RFC #8 proposes the ability for v-model to accept arguments. The argument can be used to denote the prop v-model should bind to. v-model:value="foo" compiles to:

h(Comp, {
  value: foo,
  'onUpdate:value': value => {
    foo = value
  }
})

In this case, the child component expects a value prop and emits "update:value" to sync.

Note that this enables multiple v-model bindings on the same component, each syncing a different prop, without the need for extra options in the component:

<InviteeForm
  v-model:name="inviteeName"
  v-model:email="inviteeEmail"
/>

Usage on Native Elements

Another aspect of the v-model usage is on native elements. In 2.x, the compiler produces different code based on the element type v-model is used on. For example, it outputs different prop/event combinations for <input type="text"> and <input type="checkbox">. However, this strategy does not handle dynamic element or input types very well:

<input :type="dynamicType" v-model="foo">

The compiler has no way to guess the correct prop/event combination at compile time, so it has to produce very verbose code to cover possible cases.

In 3.0, v-model on native elements produces the exact same output as when used on components. For example, <input v-model="foo"> compiles to:

h('input', {
  modelValue: foo,
  'onUpdate:modelValue': value => {
    foo = value
  }
})

The idea is to move element/input type specific handling to the runtime. For this reason, the v-model output must be something special (modelValue) for the runtime to pick up and transform. If we use the default value and input, the runtime won't know if it's created by v-model or manually by the user.

The module responsible for patching element props for the web platform will dynamically determine what actual prop/event to bind. For example, on <input type="checkbox">, modelValue will be mapped to checked and "update:modelValue" will be mapped to "change". Moving the logic to runtime allows the framework to handle dynamic cases better, and enables the compiler to output less verbose code.

Drawbacks

TODO

Alternatives

N/A

Adoption strategy

TODO

Unresolved questions

Usage on Custom Elements

It is still difficult to use v-model on native custom elements, since 3rd party custom elements have unknown prop/event combinations and do not necessarily follow Vue's sync event naming conventions. For example:

<custom-input v-model="foo"></custom-input>

Vue has no information on the property to bind to or the event to listen to. One possible way to deal with this is to use the type attribute as a hint:

<custom-input v-model="foo" type="checkbox"></custom-input>

This would tell Vue to bind v-model using the same logic for <input type="checkbox">, using checked as the prop and change as the event.

If the custom element doesn't behave like any existing input type, then it's probably better off to use explicit v-bind and v-on bindings.

@posva
Copy link
Member

posva commented Apr 9, 2019

Regarding the type="checkbox", I was thinking we could add some modifier to handle it (not sure about the naming) .checkbox, .number. That way it doesn't collide with props, although it looks less similar to regular inputs.

Regarding the default value modelValue, is there a reason not to keep the default prop to value? I think in both cases, we won't be able to help well with a migration tool and it could reduce the migration too. I do think it's a good idea to have update:value as a new default though

@yyx990803
Copy link
Member Author

@posva

Regarding the default value modelValue, is there a reason not to keep the default prop to value?

Because the idea is to move element/input type specific handling to the runtime, so the v-model output must be something special for the runtime to pick up and transform. If we use the default value and input, the runtime won't know if it's created by v-model or manually by the user.

@yyx990803
Copy link
Member Author

Published: vuejs/rfcs#31

@github-actions github-actions bot locked and limited conversation to collaborators Nov 17, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants