Extending and debugging
Debugging common problems
This page is a list of symptoms. Each one comes with what it usually means and what to do about it — most are not bugs, but a design decision meeting somebody who did not know about it yet.
Nothing happens, the HTML shows up raw
Check, in this order: the file loaded (the network tab answers that in a second); it does not
carry data-manual without a call to V.start(); and the console has no
expression syntax error, which shows up with the exact position.
If the page uses components or directives of your own registered in another file, that
registration has to happen before V.start().
The content flashes before rendering
<style>[v-cloak] { display: none !important; }</style>
<div v-cloak v-data="{}">...</div>
My CSS stopped working
Almost always a selector leaning on a v-* attribute. Once the library processes the
element, the attribute leaves the document, and [v-tab] { ... } stops matching
anything. For the same reason, el.getAttribute('v-something') returns
null and querySelectorAll('[v-show]') comes back empty.
Use classes or data-*. If you really do need the old behaviour,
V.config.cleanAttributes = false — or data-keep-attributes on the
script tag — keeps everything in the HTML.
The value changes but the screen does not
Three causes, in order of how often they come up:
-
A typo in the name. Writing to a key that does not exist creates the key in the local
scope, with no error at all. Turn on
data-devtoolsto make those cases a warning. -
You read from a source that is not reactive.
$storage.get(...),$cookie.get(...)and$url.get(...)read at the moment the expression runs, and nothing tells the screen afterwards. Keep the value in the scope, or use a store withpersist. -
It is a getter inside a store. A property declared with
getis resolved once, at creation. For derived values, use a method.
Maximum call stack, or a loop warning in the console
Some effect is writing to the same key it reads. The scheduler stops the repetition and warns.
Look the expression over: it is usually a v-effect assigning to a variable it reads
itself.
v-for loses the focus of a field when the list changes
The :key is missing. With no key, the library only knows the position, and
reordering recreates the elements. With :key="item.id", they are reused and moved.
The key has to be stable: the index will not do, because it changes along with the order.
v-if and v-for on the same element do not work
Both take control of the node. Put the condition on a child, or filter in the
v-for expression itself. The lists page shows
both ways.
window, document and fetch are undefined in expressions
That is on purpose: only a closed list of globals is exposed, and it is what makes running under
a restrictive Content Security Policy possible. To reach the DOM and services, use the magic
variables — $el, $refs, $http, $storage,
$clipboard. To expose something of your own:
V.config.globals.myFunction = myFunction;
The V object is not available inside expressions either, for the same reason.
How do I write a multi-line function in an attribute?
Do not. The parser takes expressions, not blocks. Put the logic in a method of the
v-data, of a store, or of the root scope:
V.data({ clear() { this.items = []; this.total = 0; } });
<button @click="clear()">Clear</button>
The component does not see the v-data around it
That is the default behaviour: components isolate the scope, so as not to depend by accident on
the place they were pasted into. Pass the value in as a prop, or declare
inheritScope: true in the definition.
The opposite confuses people too: the content of a <slot> belongs to the scope
of whoever wrote the tag, not to the component's. Scoped slots do not exist.
A stretch of the page disappears with no error at all
If the stretch that disappeared comes right after a component tag, look at how that tag was
closed. HTML does not accept self-closing outside the known tags:
<VInput ... /> closes nothing, and the browser puts the whole rest of the block
inside the component. Always write <VInput ...></VInput>.
The interpolation showed up raw on screen
Two causes. The first: the stretch is inside <pre>,
<code>, <script>, <style>,
<textarea> or a v-ignore, where the sweep deliberately does not
go.
The second: the text between the braces is not an expression the library can read. When parsing
fails, the stretch stays as it is, on purpose — it is what stops a lone { in a
paragraph from becoming an error on screen. An object inside the expression
({ $t('items', { n: total }) }) and a multi-line expression both work; what does not
work is what would not work in an attribute either, such as function and
new.
Known details, with a workaround
-
The confirmation shows up twice. It happens when
v-confirmsits on the same element as an HTTP verb or asv-submit: both layers read the same attribute. Keep the question in one place, usingv-clickwith$http, or$confirm(...)inside the expression itself. -
The translation disappears when the language changes.
v-t-paramsis read only on the first render. Use reactive interpolation:{ $t('items', { n: total }) }. -
The chart goes back to being a line chart when the data changes. The
v-chart-*attributes are read on mount. With reactive data, declare everything in the object:v-chart="{ type: 'bar', data: sales }". -
The extra texts of
v-confirm, such asv-confirm-title, only work withV.config.cleanAttributes = false.
The tools
When the symptom is not on this list, the reactivity inspector page shows how to watch the scopes, the effects and the requests happening — and how to install a global error handler for what slips through in production.
<div v-data="{ n: 0 }">
<button @click="n++; $log('n is now', n, 'scope:', $data)">
Add and log to the console
</button>
<p>{ n }</p>
</div>