Syntax Quirks in Vue.js
The markup used in a Vue.js .vue
file is a melange of JavaScript, TypeScript,
HTML, and a few other bits of punctuation thrown in for good measure. I
encountered one of the odd corners of this today, and as it took me an hour
to figure all of this out and
the documentation sort of hand waves around it,
I thought I should write my notes down here.
Briefly, when you’re calling a method there are not consistent rules regarding
whether or not you should put ()
after the method name. Sometimes you can
do this, and sometimes you cannot, whereas other times the behavior of the
document will be silently changed depending upon whether or not you use them.
If you have this in a template
:
<input :disabled="foo"/>
… and you know that foo
looks like:
: function() {
fooreturn false;
}
…is that template right or wrong?
Or what if the template looked like:
<input :disabled="foo()"/>
Now is it right or wrong?
The answer is: “You don’t know if it’s right or wrong, because I haven’t told
you where foo
is defined (in computed
or in methods
), and that matters
a lot. It also matters whether you place this function call in a v-on
(@
)
or a v-bind
(:
) portion of the markup.
All of the examples here are using functions which take no arguments, for simplicity. Things get even more complicated when your function requires aguments!
There’s a playground here which will help as you read this.
From that playground we can derive this table, which I will explain below:
computed /methods |
() or not? | v-on result |
v-bind result |
---|---|---|---|
computed |
no ()s | Not allowed | Works correctly |
computed |
() |
Not allowed | Syntax error |
methods |
no ()s | Works correctly | Not what you want |
methods |
() |
Works correctly | Works correctly |
If your function is defined in computed
, then using ()
will result in a
syntax error (even though you have written a function). This is at least
consistent and can be summarized concisely. Also, a function defined in
computed
cannot be used in a v-on
(@
) binding.
When you write your function in methods
things get much less clear. You can
use a method
in either a v-on
(@
) or a v-bind
(:
) binding.
For a v-on
binding, you are allowed to omit ()
, and the behavior
of the function will be exactly the same with or without it. That is:
<input @input="inputMethod"/>
…and:
<input @input="inputMethod()"/>
…will behave the same. This is “sort of” documented.
However, with v-bind
binding, if you omit the ()
then you are just
returning the method reference itself as a JavaScript expression, so if you have:
<input :disabled="disabledMethod"/>
…and:
<input :disabled="disabledMethod()"/>
…then the top example will not invoke disabledMethod
and will return the
value of the reference to disabledMethod
, which is truthy if it exists. This
is probably not the behavior you want!
The bottom example will invoke disabledMethod
and will use its result when
assigning the disabled attribute.
If any of this is unclear, try the playground , which will hopefully correct anything I’ve misstated.