TL;DR

  • Take note of your colons (:) and assignments (=) in Vue.js.

  • Colons imply an argument. Some Vue.js directives expect an argument, like v-bind.

  • Assignments do not expect an argument. v-if, v-for and friends are not expecting an argument so use =.


Yesterday I ran into a problem with a Vue.js application I was building. I wanted to do a very simple loop through an array of objects and create a drop-down list.

My code looked something like this:

<select id="account-picker" class="account-picker">
    <option v-for:"(account, index) in accounts" :value="account.id" :key="index">{{account.name}}</option>
</select>

My Vue instance looked like this:

var expenseEntry = new Vue({
  el:'#expenseentry',
  data: {
    accounts: [],
    categories: []
  },
  template: ...
})

So, if I put some objects in the accounts property of the object expenseEntry, I should see my drop-down list get populated with a new option. Pretty simple!

Except I was getting an error stating:

Property or method 'account' is not defined on the instance but referenced during render.

This sent me reading and re-reading my code trying to figure out why Vue thought account was not defined. account is the alias for the current element as Vue iterates through the accounts array.

"Of course account is there, silly Vue! You're the one who's making the variable!" I muttered to myself.

Of course, there are lots of sources of good help out there. Of particular note was Michael Thiessen's article "Property or Method is Not Defined". In it, he offers 2 good solutions to the problem. However, there's a 3rd that we might also include:

The solution to my problem was based on a small source of discomfort I am having with Vue, in general: the ':' and '=' syntax.

Which do I use: ':' or '='?

Here are some typical Vue.js template lines from the documentation:

A conditional:

<p v-if="seen">Now you see me</p>

The v-html directive that replaces mustaches:

<p>Using v-html directive: <span v-html="rawHtml"></span></p>

The v-bind directive that lets you assign and use variables in the DOM element attributes:

<div v-bind:id="dynamicId"></div>

And here's a click event:

<a v-on:click="doSomething"> ... </a>

Finally, here's the all-important v-for directive:

<li v-for="item in items">{{ item.message }}</li>

Do you see the issue I was having? It appears to be an inconsistency in the syntax which is hard for me to remember: "When do I use the ':' after the directive and when do i use the '=' after the directive? This little thing tripped me up in my code above and was causing the error message.

So, how do I grok when to use ':' and '='?

Arguments

The key point to understand is this: directives like v-if, v-html and v-for are not expecting an argument while directives like v-bind do expect an argument.

Arguments in Vue are denoted by a colon ':' after the directive. This is in the same way as arguments to a method in many languages are denoted by parens (). This is just a thing that I have to remember.

Now, take a look at my code again:

<select id="account-picker" class="account-picker">
    <option v-for:"(account, index) in accounts" :value="account.id" :key="index">{{account.name}}</option>
</select>

Vue is going along, sees my v-for with the trailing colon and starts looking for an argument (even though v-for doesn't want any). It parses the next token which is account. But at this point account hasn't been defined yet and will never be because I broke the syntax.

What I should have done was replaced the ':' with a '=' and that fixed everything.

For clarity, let's take a look at a v-on directive.

<a v-on:click="doSomething"> ... </a>

In this case, the ':' is logical because the argument to the v-on directive is the type of event being registered - in this case a 'click' event.

Conclusion

Don't let the ':' and '=' trip you up. If you get the Property not found error it just might be that you've used the wrong character!