Closed sifex closed 5 years ago
A good example of the v-model.lazy problem that occurs when you do this: https://i.imgur.com/Ei336EM.gifv
I don't really understand the need for this, because Vue's computed properties can just access vue-mc state and still be "computed" and memoized within Vue itself. I'd be interested to hear what problem this solves, or why attributes + computed in Vue does not cover the use case that motivated this PR.
I don't really understand the need for this, because Vue's computed properties can just access vue-mc state and still be "computed" and memoized within Vue itself.
Good point, right now without memorisation it's hard to see why this is any better than they're alternatives. It essentially revolves around one-way data-binding (for get-only computed properties) and to be able to control the flow of data better with bi-directional bindings.
I recently came into this one regarding moment and timezone conversion. Because I want to convert the timezone in the vue component to the user's local time zone, whilst keeping the time zone within the model in GMT/UTC (same as the API endpoints requests).
Given moment.tz(..., 'UTC')
is used for interpreting time, and moment(...).tz('Sydney/Australia')
is used for shifting time-zones – An example of this is shown here:
export default class Membership extends Model {
...
mutations () {
return {
current_period_end: (currentPeriodEnd) => moment.tz(currentPeriodEnd, 'UTC')
}
}
...
}
Next renewal date is <b>{{ membership.current_period_end.tz($local_tz).format("dddd, MMMM Do YYYY, h:mm a z") }}</b>
The above example surprisingly causes an infinite loop within Vue, as .tz('Sydney/Australia')
caused a mutation in timezone to current_period_end
, which was not the same output as current_period_end.tz('UTC')
, and got recomputed back and forth between the two timezones.
One fix for this was to import moment
into the component's method and clone the moment instance:
Next renewal date is <b>{{ moment(membership.current_period_end).tz($local_tz).format("dddd, MMMM Do YYYY, h:mm a z") }}</b>
As you said, you can also create a computed property of the membership.current_period_end
to the formatted current timezone, which is fine, but grows a pain once you start doing this for every component that shows this. Computed (forked version for me) looks like this:
export default class Membership extends Model {
...
computed () {
return {
moment_period_end: () => moment.tz(this.current_period_end, 'UTC')
}
}
...
}
Next renewal date is <b>{{ moment_period_end.tz($local_tz).format("dddd, MMMM Do YYYY, h:mm a z") }}</b>
This is kinda where it makes more sense – where 'computed' probably loses its original meaning, and it more acts as a translation layer, could be called "translations".
I have an input that takes it's value in dollars (user input) called cost_in_dollars
and the model that takes the value in cents called cost
. There's a need to translate back and forth such that mutations definitely causes a hassle. (Something functionally equivalent to v-model="{ in: x => x * 100, out: x => x / 100"
)
export default class Plan extends Model {
...
computed () {
return {
cost_in_dollars: {
get: () => parseFloat(this.cost / 100).toFixed(2),
set: (dollars) => { this.cost = parseInt(dollars * 100) }
}
}
}
...
}
Now I can do v-model="plan.cost_in_dollars"
and it's all handled outside of the component level.
Hopefully this makes sense
Found another big one around input translation, where "25/06/1953"
should be converted in (v-model:lazy) realtime to a Date Object in the Model. The forward translation should be Date
formatted to "25/06/1953"
I see what you mean now. It seems to me though that you're using the model for component logic. The model should really only be a data model with minimal logic. In your example for cost
, where the model stores cents but the input is dollars, I would expect to see something like this in the component.
computed() {
return {
cost {
get: () => plan.cost / 100,
set: (v) => plan.cost = v * 100,
}
}
}
You could have methods on the model like setCostFromCents
or setCostFromDollars
, but to override the data attribute in the model directly doesn't make as much sense to me.
So the suggestion put forward is to do the translations in the component, according to the requirements of the component. The vue-mc model is like a data-transfer / named object that tries to keep as close to its raw data model as possible. Maybe mutations
were a mis-step also.
We're not closed to this idea though, just not sure it has a place yet.
Thanks @rtheunissen for looking over this 😊 I'll see if I can find a way to abstract this away from both the component layer and the data layer.
Synopsis
vue-mc
computed()
and_computed
method / vue attributeObject.defineProperty(this, attribute)
, separate from _attributesExample
Todo:
v-model.lazy
when using mutations & computed setters – otherwise your setter will try to fix the value as the user types.Future Plans