V-model updates all attributes on keydown

Hello,
I never realised that binding a v-model is calling all attributes on the input on keydown

<input type="text" v-model="username" :placeholder="getPlaceholder()" />

for example when i would type in the input, the getPlaceholder method is called everytime.

if i would use:

<select v-model="usernames">
  <option v-for="option in getAsyncOptions()" v-bind:value="option.value">

this means the getAsyncOptions methods is called on every keydown?

thank you

It isn’t just v-model. When any reactive dependency of the template changes the entire template will re-run.

Templates are compiled down to render functions. Dependency tracking for render functions is very similar to tracking dependencies for computed properties. The tracking is started before render is called and anything that is read is added as a dependency. There is no attempt to keep track of precisely how that dependency was used. If the dependency subsequently changes, the render function will be called again, running in its entirety.

The render function itself creates VNodes, which are used to generate and update the desired DOM. If the VNodes haven’t changed then the DOM won’t be touched. The overhead of running the render function is typically small compared to the cost of changing the DOM.

On the rare occasions that the render function itself is a performance problem, the first strategy to improve that is to split it up into multiple components. Separate components have separate render functions and each one will only run if a dependency has changed.

Running the render function will call any methods it needs. This happens every time it runs. It isn’t limited to specific elements, all the methods from the template will be called every time it runs.

Templates and render functions are expected to be free of side-effects. They should reflect the current state, not change it. While you haven’t explicitly stated what getAsyncOptions does, its name implies it has side-effects, so it wouldn’t be appropriate to call it from a template.

In general, if you find yourself calling methods during rendering without passing any parameters then you should probably be using computed properties instead. Again, everything is assumed to be free of side-effects.

If you need side-effects then they should be triggered in other ways. e.g. In lifecycle hooks, watchers, event listeners or setters.

I am working with Vue for 1.5 year but somehow i missed this crucial piece of knowlegde:

Templates and render functions are expected to be free of side-effects

Thank you for clearing that up :clap:
Till now / or yesterday, for example i’m used to calling methods on generating forms within templates

<template v-for="(field,i,n) in form.fields">
 <input 
  v-model="field.model"
  :type="field.type
  :options="getOptions(field)"
  :label="$t(field.label)"
  :key="`field${n}`
/>
</template>

Where $t is an internationalisation plugin (vue i18n), which gave me a warning everytime a translation string was missing onKeyDown within this input field.
This i think is at low cost when compiling the template and the most easy way of binding form labels

And when I added options in getOptions from a rest/api i noticed that the request was fired on every keyDown

So since my form is defined in the vue Data, and have multiple options generated from a rest/api. Some different approaches to be free of side effects would be:

  • to iterate those fields on created or mounted, and replace the field options object with their (rest) provided results
  • using :value instead of v-model, and collect the values on submit with $refs

I’m not sure how changing v-model to :value would help to avoid side effects. Surely that would just avoid the template running during typing? There would still be side effects the first time it runs. The point of being side-effect free is that the template can be run at any point without having to worry about the impact on anything else.

You are right, using :value but still calling methods to generate form options directly in the template doesn’t make sense.

I was thinking of creating a form component that creates the inputs based on it’s parent form-data (fieldtypes and formvalues), fromout a prop e.g formdata

So how to achieve this without side-effects.

  1. clone the formdata prop on mounted, within the form (child) component
  2. generate form options and add them to my cloned formdata with methods within my form component. let’s say the form options are colors fromout a rest-api
  3. in the template i can use v-model’s and bind them to the cloned formdata object
  4. on form submit I emit the cloned formdata to it’s parent
  5. in the parent component i could send the data to any endpoint etc, and merge the results within the (parent) form-data object
  6. then within the form child component i would need a (deep) watcher to check if the formdata is changed, to start with step 1. again

Or am i over complicating things? The point is that adding the form options within the parent component would result in duplicate methods, if i would have other pages with forms and lot’s options

The overall approach sounds about right.

There’s no need for the options loaded from the server to be included in the formdata but if that’s the easiest way to implement it then go for it. Personally I’d be inclined to keep the editable parts separate from the static meta details.

During emit you need to be careful about exactly what object you emit. You probably don’t want the parent and child both having a reference to the same object. I would typically clone the object prior to emitting.

For doing the prop cloning, mounted is too late. You need to do it pre-render. So either inside data or created. Given you plan to use a watch I suggest going with a method called from created. That same method can then be called from the watch. The method doing the copying may also be the right place to trigger the server calls, though I don’t know enough about how everything fits together to be sure about that.

Maybe, but if it’s going to be reused across multiple pages then it sounds like a good idea to me.

As starting with Vue a while a go I often directly went into specific parts of the documentation to get the work done. With good results in the browser, till my apps grow to the next level.

Thank you for pointing me out some very important basics and aspects about vue