Skip to content
New issue

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

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

Already on GitHub? # to your account

Should v-model-* directives be public? #156

Closed
jods4 opened this issue Apr 8, 2020 · 17 comments
Closed

Should v-model-* directives be public? #156

jods4 opened this issue Apr 8, 2020 · 17 comments

Comments

@jods4
Copy link

jods4 commented Apr 8, 2020

In this comment Evan pointed out to me that vModelText (and friends) is exported for the compiler only and is not public nor documented.

Unless I miss something, this has an unfortunate consequence: you can't write a JS render function that binds v-model of an input.

Because this template:

<input v-model="name">

is compiled into the JS equivalent of this by the compiler:

<input v-model-text="name" @update:modelValue="name = $event">

and you can't do that transformation in user code if v-model-text is off-limits.

Alternatives

  1. Use undocumented apis 🤞
    Drawback: risks breaking at any minor release.

  2. Re-implement v-model yourself by compiling

<input :value="name" @input="name = $event.target.value">

Drawback: this is not trivial when implementing more complex features, such as modifiers v-model.trim, or other models e.g. v-model-checkbox with an array.

  1. Create a micro-component with compiler and re-use it in your render function.:
<input v-model="$props.name" v-bind="$attrs">

Drawback: far from pretty.

@leopiccionia
Copy link

In Vue 2, we used to do the second option i.e. manually handle attributes and events.

Eventually, a Babel transform for vModel in JSX (with modifiers et al.) became officially supported (and included in Vue CLI, Nuxt, etc., if I remark correctly).
https://github.com/vuejs/jsx/blob/dev/packages/babel-sugar-v-model

As soon as the JSX plugins are completed, I can see it becoming supported in Vue 3 as well.

@jods4
Copy link
Author

jods4 commented Apr 8, 2020

It's nice that there is an official way but we don't use JSX here and we are not really considering it for now -- although that may not be a total blocker.

On the other hand, our pipeline is Typescript for transpilation into Webpack for bundling. A Babel plugin is out of question for us.

(We currently go with 1)

@leopiccionia
Copy link

TypeScript and Babel are not incompatible here. TS allows you to preserve JSX for further transpiling.
https://www.typescriptlang.org/docs/handbook/jsx.html#basic-usage

Actually, Vue CLI 3's TypeScript emits ES2015 code and pipes it to Babel plugin.
https://cli.vuejs.org/core-plugins/typescript.html#configuration

@jods4
Copy link
Author

jods4 commented Apr 8, 2020

It's not a compatibility problem.

It's that it's totally out of question for us to pipe Babel into our build pipeline just for that.
We already use Typescript to transpile our code and we don't need a second transpiler.
It's that many additional dependencies, build time and problems.

@CyberAP
Copy link
Contributor

CyberAP commented Apr 9, 2020

Built-in vModels are bound to HTML native elements, so in order to reuse them you'll have to place them on these elements. So exposing their logic out doesn't really make sense since you can't apply it to any arbitrary element.

The problem with v-model is that it is a two way binding and will mutate state. That means you can't directly use it on a prop. To actually be able to do so you'll have to work with computed getter and setter.

@jods4
Copy link
Author

jods4 commented Apr 9, 2020

@CyberAP I don't understand your answer.

I'm saying it's an unfortunate limitation that I can compile this html template:

<input v-model="name">

but I can't write a JS render function that performs the same.

Actually, I can but it's not officially supported:

function render() {
  return withDirectives(
    h("input", { "onUpdate:modelValue"(v) { name = v } }), 
    [ [ vModelText, name ] ])
  )
}

@CyberAP
Copy link
Contributor

CyberAP commented Apr 9, 2020

But doesn't vModel automatically pick which model to use?
You could write this and it should work:

function render() {
  return withDirectives(
    h("input", { "onUpdate:modelValue"(v) { name = v } }), 
    [ [ vModelDynamic, name ] ])
  )
}

Providing some guidance on how to use it would be helpful, in that I agree.

@jods4
Copy link
Author

jods4 commented Apr 9, 2020

What I wrote works as well.
The real question is: is vModelDynamic officially supported in user code?

I find it strange that vModelDynamic is a public API and the other are not. They're really all the same level of abstraction.

@CyberAP
Copy link
Contributor

CyberAP commented Apr 9, 2020

That doesn't sound strange to me, it's a sort-of a facade pattern and works pretty well to abstract all the different vModels Vue uses internally. I don't see why end user should know about them because they are implementation details. You don't write <input v-model-text="value"> in your templates, why you should do that in your render functions? What is important is that all of them provide a common interface.

@jods4
Copy link
Author

jods4 commented Apr 9, 2020

@CyberAP you don't write v-model-dynamic in your template either. Your write v-model and the compiler performs magic, using v-model-text. The compiler magic, you'll have to do yourself.

There's a strong mismatch in v-model-dynamic as a public API vs what it's used for internally.
Internally the name and what it does match perfectly.
As a public API: just no. You're telling people to use it publicly in a different way than it's used internally, with a naming that doesn't match intention.
It works, but it's definitely not good api design, if only because of naming and inconsistency in usage.

Now my thinking here is: if Vue thinks v-model-dynamic is ok as a public API, which means it's gonna be supported and not have breaking changes, why not support v-model-text and friends publicly? They'll all evolve together. It's highly unlikely that v-model-text gets breaking changes but v-model-dynamic stays the same, esp. because the later is a direct proxy for the former.

@leopiccionia
Copy link

Reading Evan's comment that you linked, I can't see any mention that vModelDynamic is public API, either. Why do you think this is the case?

@jods4
Copy link
Author

jods4 commented Apr 9, 2020

@leopiccionia Assuming you're adressing me because you said "Evan's comment that you link": I don't.
@CyberAP suggested it.

@leopiccionia
Copy link

Update: By this commit, all vModel* directives, including vModelDynamic, are marked as private APIs.
vuejs/core@c9bf7de

@jods4
Copy link
Author

jods4 commented May 4, 2020

It's great that there is now a very clear indication of what is exported but still considered internal and shouldn't be relied upon.

It leaves the original question very much unanswered, though: there's no good and easy way to bind a textbox from a render function in the same way the compiler (template or JSX) does.
This is especially true if you try to bring in some of the more subtle modifiers, such as .trim (it's less intuitive than you might think).

@jods4
Copy link
Author

jods4 commented May 15, 2020

Because there are now 2 other issues / PR that tackle "how to create components that wrap an input" I'm gonna add here my original use-case for this.

I'm using the v-model internals to passthrough a v-model inside a component that wraps an input.

Basically I'm doing this:

<template>
  <input 
    v-model-text.trim="$attrs.modelValue"
    @onUpdate:modelValue="$attrs['onUpdate:modelValue']"
    />
</template>

And my custom input can be simply used like: <my-input v-model='xyz'>

That's efficient and simple, although a bit obscure and relies on currently internal apis.

Not saying it's the best way but there's a demand for simple model wrappers, see those alternative ideas: #175, #165, #140, #21 .

@jods4
Copy link
Author

jods4 commented Jun 10, 2020

@yyx990803 Looks like this was fixed by vue-next#1329, right?

@yyx990803
Copy link
Member

Yes. v-model and v-show are now public after e4dc03a8

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants