Giter Club home page Giter Club logo

Comments (11)

yyx990803 avatar yyx990803 commented on May 5, 2024

Actually with v3's flat vnode data implementation, declaring props is unnecessary. When you pass data down it doesn't matter if it's a prop or not.

So the wrapper can simply do

<input v-bind="$attrs">

Regarding the input event re-emit, I kinda want to avoid this event vs. value mismatch altogether. Maybe the code v-model generates should just handle both event and plain values. e.g. value = value instanceof Event ? value.target.value : value

from core.

chrisvfritz avatar chrisvfritz commented on May 5, 2024

Actually with v3's flat vnode data implementation, declaring props is unnecessary. When you pass data down it doesn't matter if it's a prop or not.

Yay! 😄

Regarding the input event re-emit, I kinda want to avoid this event vs. value mismatch altogether. Maybe the code v-model generates should just handle both event and plain values. e.g. value = value instanceof Event ? value.target.value : value

I think that'd be great, but I still feel like the default event for components should be update:value. It's not only more consistent with other 2-way bindings, but also feels more semantically accurate for a component, since updates may be triggered by things other than "input" from the user (e.g. responses from an API, images loading, etc).

Making v-model smarter with components

One other thing I think we need to really improve v-model with components is detecting the kind of element that arbitrary attributes are passed through to. That way, v-model can be as smart with components as it is with normal elements and always "just work", using the correct attribute and event. (This would also eliminate the need for the model option on components.)

Otherwise, I worry it might be confusing if, depending on the element, v-bind="$attrs" sometimes just works (e.g. <input type="text">), but often doesn't work (e.g. <select>, <input type="checkbox">, etc). In my experience, most developers don't know the exact attributes and events necessary to work with every form element, so offering the same convenience for components that we do for elements would save a lot of development hours.

from core.

yyx990803 avatar yyx990803 commented on May 5, 2024

@chrisvfritz yeah, I agree regarding emitting update:value instead. #8 also makes sense.

Making v-model smart can be tricky, because we can't know at compile time what element the $attrs will be spread on in the child component.

One possible implementation: v-model generates code like this:

<input v-model="foo">
h('input', {
  modelValue: this.foo,
  'onUpdate:modelValue': value => {
    this.foo = value
  }
})

When used on a component:

  • A component that expects to work with v-model should expect a prop modelValue
  • To trigger update, it should emit an event named update:modelValue

When used on an element, the runtime will dynamically transform modelValue and update:modelValue to appropriate values depending on target element type when patching the element:

  • modelValue -> value or checked based on element type
  • update:modelValue -> input or change based on element type, also handles extracting the value from raw DOM event.

This way, v-model will generate the exact same code when used on elements AND components. A component with v-model can simply spread $attrs on any element and it will just work.

from core.

chrisvfritz avatar chrisvfritz commented on May 5, 2024

@yyx990803 Brilliant! I love it. 😻 A few follow-up questions:

  • Should v-model="foo" actually translate to an onModelUpdate:value event (instead of onModelUpdate:foo)?

  • For consistency, should v-model:my-prop="foo" translate to modelMyProp and onModelUpdate:myProp, which could also be transformed at runtime to the correct attribute and event?

  • Should registering myProp as a prop also automatically register:

    • modelMyProp
    • onModelUpdate:myProp
    • onUpdate:myProp


    That way, all of these would automatically stay out of $attrs. Then modelMyProp and onModelUpdate:myProp could just be aliases for myProp and onUpdate:myProp, but with special behavior when bound to an element. Would that make sense?

With those final tweaks, I think we might actually have the perfect v-model API!

  • The simplest cases remain as simple as possible: just v-model="foo" on an element, with v-bind="$attrs" added for a component.

  • In cases where users want to manage v-model behavior for a prop manually, they can just use myProp and update:myProp without having to worry about the special model/onModel-prefixed attributes.

  • When users need full access to v-model's magic on an element, they can choose to explicitly use modelMyProp and/or onModelUpdate:myProp to achieve the exact behavior they're looking for, with minimal boilerplate.

from core.

yyx990803 avatar yyx990803 commented on May 5, 2024

I just edited my proposal a bit. When used without an argument the generated props should not care about what key it is.

When used without an argument:

<input v-model="foo">
h('input', {
  modelValue: this.foo,
  'onUpdate:modelValue': value => {
    this.foo = value
  }
})

When used with an argument it will compile like this:

<Comp v-model:someProp="foo">
h(Comp, {
  someProp: this.foo,
  'onUpdate:someProp': value => {
    this.foo = value
  }
})

So someProp is passed down with value bar, and the child can emit update:someProp to perform 2-way sync.

I don't think it makes sense to use v-model with arguments on a plain element, it seems the use case is really for two-way binding on component only. So the runtime special handling will only apply to modelValue and onUpdate:modelValue.

from core.

chrisvfritz avatar chrisvfritz commented on May 5, 2024

I don't think it makes sense to use v-model with arguments on a plain element, it seems the use case is really for two-way binding on component only. So the runtime special handling will only apply to modelValue and onUpdate:modelValue.

@yyx990803 I agree it doesn't make sense to use v-model with arguments on plain elements, but I was thinking about cases where a user might still want to bind props to individual elements within a component, e.g.:

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

where InviteeForm is defined as:

<script>
export default class InviteeForm extends Component {
  static props = ['name', 'email']
} 
</script>

<template>
  <form v-bind="$attrs">
    Name: <input v-bind="{ modelName, 'onModelUpdate:name' }">
    Email: <input v-bind="{ modelEmail, 'onModelUpdate:email' }">
  </form>
</template>

Though maybe we could actually abstract away the implementation details of:

<input v-bind="{ modelName, 'onModelUpdate:name' }">

by simplifying to something like:

<input v-model.prop="name"> 

What do you think?

from core.

Jinjiang avatar Jinjiang commented on May 5, 2024

Sorry. Why not use value directly but choose modelValue by default? Did I miss something?

from core.

chrisvfritz avatar chrisvfritz commented on May 5, 2024

Why not use value directly but choose modelValue by default?

@Jinjiang I don't think I understand the question. Could you elaborate?

from core.

Jinjiang avatar Jinjiang commented on May 5, 2024

Sorry for my bad description. 😅

As we discussed above, now

<input v-model="foo">

will equal to:

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

not:

h('input', {
  value: this.foo,
  'onUpdate:value': value => {
    this.foo = value // or `value = value instanceof Event ? value.target.value : value`
  }
})

right? So my question is why we choose the name modalValue but not value here?

from core.

chrisvfritz avatar chrisvfritz commented on May 5, 2024

@Jinjiang Ah, I see. If I understand correctly, it's because modelValue has special behavior specific to the element it's added to. For example, on a <select> element it will not map to a value attribute, but instead add selected to the appropriate <option>. Does that make sense?

from core.

yyx990803 avatar yyx990803 commented on May 5, 2024

Closing in favor of vuejs/rfcs#8 and vuejs/rfcs#31

from core.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.