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

Type transformation modifiers for v-model #2002

Closed
rpkilby opened this issue Dec 10, 2015 · 23 comments
Closed

Type transformation modifiers for v-model #2002

rpkilby opened this issue Dec 10, 2015 · 23 comments

Comments

@rpkilby
Copy link

rpkilby commented Dec 10, 2015

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
Copy link

Morgul commented Dec 10, 2015

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

@davidkhess
Copy link

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

@posva
Copy link
Member

posva commented Mar 31, 2016

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

@Morgul
Copy link

Morgul commented Mar 31, 2016

@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
Copy link

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

@posva
Copy link
Member

posva commented Mar 31, 2016

@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
Copy link

Morgul commented Mar 31, 2016

@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
Copy link
Author

rpkilby commented Apr 2, 2016

@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,

  • momentjs transform instead of a potentially built in date transform
  • JSON transform

@posva
Copy link
Member

posva commented Apr 3, 2016

@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
Copy link
Author

rpkilby commented Apr 3, 2016

@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.

  • Boolean values and array selection are handled magically for inputs of type="checkbox" (and to be fair, this is kind of necessary).
  • Number values are not handled magically for inputs of type="number", and require a number paramater.
  • Any other data type requires a two-way filter.

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" />
  • Given that checkbox has magical behavior, it's understandable that someone would expect that number would have magical behavior as well.
  • Forgetting to add the number parameter is an easy source of bugs.
  • Why does number get special treatment anyway? Should it be replaced by a filter?
  • Filters seem like they should be used for value transformations, while this request is more about type transformation.
  • I also don't like the idea of polluting the filter namespace with type transformation filters. Out of a form context, they don't make much sense. At the very least, it's great that Vue has global and component-local registration, so this isn't really an issue.

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:

  • Regardless of opinions on the above, I do think it would be good to add a section on two way filters to the form docs as a way of handling type conversion.
  • Also, really want to reemphasize that these are very minor consistency/API nitpicks. Vue has been a huge boon and the existing form bindings work.
  • One downside to the proposal is that there would be some overlap between two-way filters and v-model type transforms.
  • I apparently like bulleted lists

@davidkhess
Copy link

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

@yyx990803
Copy link
Member

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

@rpkilby
Copy link
Author

rpkilby commented May 3, 2016

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

@davidkhess
Copy link

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

@yyx990803
Copy link
Member

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
Copy link

Morgul commented Jun 16, 2016

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
Copy link
Member

@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
Copy link
Author

rpkilby commented Jun 17, 2016

For argument:

  • Our API uses the iso 8601 format to serialize dates.
  • Client-side data model deserializes date strings into native Date objects.
  • The application code expects to interact with Date objects.

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.

  • No template changes were required (still just v-model="someDate | date")
  • The filter is DRY - just had to change the read/write functions.

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

  • Each new date input would require additional boilerplate for setting up a two-way computed prop.
  • Computed properties aren't DRY. Updating from Dates to moments would require globally updating each computed property. Probably not a huge issue in practice, but definitely more prone to error.

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.

  • Transforms are a v-model directive modifier and do not otherwise complicate the template syntax.
  • The modifier syntax does not permit additional arguments in the template, which would keep transforms simple. You get an input value, produce an output value.
  • Similar to filters, I'd agree that Vue shouldn't provide builtin transforms (except for number).

Possible API:

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

  parse(value) {
    return value ? new Date(value) : undefined;
  },
});

@franciscolourenco
Copy link
Contributor

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

@ecmel
Copy link

ecmel commented Sep 13, 2016

@rpkilby Opened #3666

@Mouvedia
Copy link

Mouvedia commented Feb 5, 2018

A boolean modifier would have been great.

@xor-gate
Copy link

xor-gate commented Aug 1, 2018

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

@BrunoVGG
Copy link

BrunoVGG commented Aug 1, 2018 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

10 participants