vuejs / vue

This is the repo for Vue 2. For Vue 3, go to https://github.com/vuejs/core
http://v2.vuejs.org
MIT License
207.99k stars 33.69k forks source link

Type transformation modifiers for v-model #2002

Closed rpkilby closed 8 years ago

rpkilby commented 8 years ago

From @Morgul's comment in #1713:

<!-- Cast to number -->
<input type="number" v-model.number="foo">

<!-- Cast to boolean -->
<input type="text" v-model.boolean="bar">

It would also seem useful to be able to register custom transformations. eg, datetime transformations for Moment.js objects.

Morgul commented 8 years ago

@rpkilby Thanks for creating this issue, I was planning on tackling it to day, but work got in the way. :)

davidkhess commented 8 years ago

Curious - couldn't this just be done with custom two-way filters? Maybe I'm missing something.

posva commented 8 years ago

@davidkhess Indeed, it seems possible: https://jsfiddle.net/posva/8whzuL16/1/

Morgul commented 8 years ago

@davidkhess Yes, it's possible. This issue, however, is about adding built-in support.

The problem I've had with this approach is that it's unexpected, coming from other frameworks. All of them 'have the magic built in' to handle number inputs. Writing your own filter to handle this, while possible, is very sub-optimal. The compromise between what @yyx990803 envisions and what I'd like seems to be adding transforms for v-model.

davidkhess commented 8 years ago

@Morgul completely understand – I tend to favor less magic.

posva commented 8 years ago

@rpkilby @Morgul Do you have a real use-case about any modifier you think should exist?

There is already a number parameter for text inputs: http://vuejs.org/guide/forms.html#number Isn't it enough?

Morgul commented 8 years ago

@posva Not really, no. As discussed in #1713, it's surprising when you're getting a number out of a number input. While vue has a way of handling that (the number directive), we discussed the fact that it could be better implemented as a transform, because then vue's syntax for handling these sorts of issues is consistent. This is the exact type of thing transforms were made for; number should be one.

rpkilby commented 8 years ago

@posva - It seems like a date transform for the date/datetime input types would be useful. However, I'm more interested in being able to register transforms. eg,

posva commented 8 years ago

@rpkilby I think two way filters work out pretty well for this. The guide actually talk about this http://vuejs.org/guide/custom-filter.html#Two-way-Filters Maybe a more explicit example showing v-model="when | date" in the docs help out. I don't think a modifier is needed because two way filters already address this issue

rpkilby commented 8 years ago

@posva - yes, and we currently do this. I guess my problem is that I have vague (and I want to emphasize, very minor) dissatisfactions with the API for dealing with various data types. I have some thoughts that haven't really formed into anything cohesive.

The result of all of these leaves us with:

<input type="checkbox" v-model="foo" />
<input type="number" v-model="bar" number />
<input type="date" v-model="baz | date" />

Example:

<input type="text" v-model="title" />
<input type="text" v-model="slug | lower | kebab" lazy />
<input type="date" v-model.date="publishOn" />
<input type="number" v-model.number="priority" />

Edit:

davidkhess commented 8 years ago

@rpkilby very nice analysis and writeup. Agreed, things are inconsistent.

yyx990803 commented 8 years ago

@rpkilby thanks for the thoughtful input - we'll definitely improve this in 1.1 :)

rpkilby commented 8 years ago

@yyx990803 any new thoughts on this given the deprecation of filters outside of interpolated text in Vue 2.x?

davidkhess commented 8 years ago

Ditto, removing filters seems to beg for some hook on v-model to accomplish type/value transformations.

yyx990803 commented 8 years ago

The problem with other type transformations other than .number is that the conversion from the value back to string is not straightforward. e.g. for a date transformer, if you programmatically set the bound value to a Date object, there's no way for Vue to know how to display it as text in the input box. Then again we are back to similar concepts as two-way filters.

Type transformations also by definition makes the view out of sync with the underlying state.

My take on this use case is that instead of the implicit magic conversions, let v-model just sync what the user actually inputs with an underlying value (the source of truth), and then build derived values separately based on that value.

For example, you can perform these transformations only when you need to send it to the server for persistence. Or, use computed properties based on the bound value if you need to display it elsewhere in a different format.

Morgul commented 8 years ago

My take on this use case is that instead of the implicit magic conversions, let v-model just sync what the user actually inputs with an underlying value (the source of truth), and then build derived values separately based on that value.

Preface: upon rereading, it's unclear if you're talking about removing the number directive or not, but my argument can be applied to any sort of transformation between a form's data and what's stored in a model, not just numeric inputs.

For simple use cases, this may be sufficient (and in some cases preferred). However, I have a use case with about 60 <input type="number"> fields. A rare case, admittedly, but it's what I've got. Since I need to perform math operations on the user input, I would end up with significantly more than 60 computed properties for those operations, and then the code to save to the server would be an annoying (but not complex) merger of the computed properties and the underlying data object.

I agree in principal with "let v-model just sync what the user actually inputs", however my contention is that when a user is presented with a <input type="number"> field the user's intention is to input a number, so Vue should store it as a number. The user doesn't know that HTML5's spec for number inputs is weird; They were asked for a number, so they input a number.

I'm fine if I have to do some work somewhere (specifying a transform, a two way filter, or something like that), but I don't want to have to define 60+ computed properties and then write code to try and merge those computed properties with another object just so I can turn it into JSON to send to my server. It feels like an inelegant solution, compared to other frameworks.

My use case is numeric inputs, but being a little more pedantic, you could apply the same logic to the values for check boxes (true/false vs string values), date fields, radio buttons, etc. Any time you have a large number of properties that need special handling, you'll run into this problem. I just don't think that 'use a computed property' scales well.

I'd be a lot more sanguine if it felt like there was a clear path forward that had the same elegance I'm used to in the rest of the framework.

yyx990803 commented 8 years ago

@Morgul .number is still available because the conversion from a number to a string is very straightforward. That is not the case for other arbitrary types, e.g. Date.

rpkilby commented 8 years ago

For argument:

you can perform these transformations only when you need to send it to the server for persistence. Or, use computed properties based on the bound value if you need to display it elsewhere in a different format.

For a one-off type transformation this is fine, but as @Morgul said, this is problematic at scale. In our latest project we dealt with a lot of dates. In lieu of type transforms, we used two-way filters. At some point, we made the switch to using moment.js. Updating the filter was incredibly simple.

In contrast, using computed properties would have been more painful:


Other thoughts:

Type transformations also by definition makes the view out of sync with the underlying state.

I would disagree - the view is a representation of the underlying state. The type transformation simply transforms an unrepresentable type (Date object) into something that is representable (date string).

if you programmatically set the bound value to a Date object, there's no way for Vue to know how to display it as text in the input box. Then again we are back to similar concepts as two-way filters.

Type transformations are definitely similar to two-way filters, however they seem like they would be much more simple. An intentionally minimal API makes sense.

Possible API:

Vue.typeTransform('date', {
  render(value) {
    return value ? value.isoformat() : '';
  },

  parse(value) {
    return value ? new Date(value) : undefined;
  },
});
franciscolourenco commented 8 years ago

This would be in line with the coerce functionality in component props.

ecmel commented 8 years ago

@rpkilby Opened https://github.com/vuejs/vue/issues/3666

Mouvedia commented 6 years ago

A boolean modifier would have been great.

xor-gate commented 6 years ago

This works perfect, indeed a boolean modifier would also be a nice addition 👍

BrunoVGG commented 6 years ago

Az

On Wed, 1 Aug 2018, 18:31 Jerry Jacobs, notifications@github.com wrote:

This works perfect, indeed a boolean modifier would also be a nice addition 👍

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/vuejs/vue/issues/2002#issuecomment-409656290, or mute the thread https://github.com/notifications/unsubscribe-auth/AOYJoQCojZNAjrbZxY_4QLHVHItaRZCqks5uMeYLgaJpZM4Gy4Xw .