Vue.js 3 Functions/Methods Tutorial

In this tutorial we learn how functions work in Vue.

We cover how to reference data properties, use parameters and the problems we may encounter with arrow functions.

Finally, we cover a common issue with referencing and invoking functions.

Lesson Project

If you want to follow along with the examples in this lesson, you will need an app generated by the Vue CLI . If you already have one from the previous lesson, you can use it instead.

Functions in Vue

At some point, our application will need more complex logic. We may want to perform operations on data, or simply separate some code.

Vue gives us the methods option for the config object to store all the functions we want to use in a component.

The methods option expects an object as its value and inside it we can define comma separated standard Javascript functions without the function keyword.

Syntax: methods
<script>
export default {
  methods: {
    funcOne() {},
    funcTwo() {},
    funcSoulBrother() {}
  }
}
</script>

note The functions we define here are only available to the current component, even if that component is imported into another.

As an example, let’s create a function that returns a string greeting message, then invoke the function in moustache syntax in the template to output it to the browser.

Example: src/App.vue
<template>
  <p>{{ greeting() }}</p>
</template>

<script>
export default {
  methods: {
    greeting() {
      return 'Hello there'
    }
  }
}
</script>

When we run the example in the browser, we’ll see the greeting message.

Reference data properties with this

Let’s say we want to personalize the greeting example with a username defined as a data property.

Example: src/App.vue
<template>
  <p>{{ greeting() }}</p>
</template>

<script>
export default {
  data() {
    return {
      username: 'John'
    }
  },
  methods: {
    greeting() {
      return 'Hello there ' + username
    }
  }
}
</script>

When we try to save the file, Vue raises an error.

Output:
Failed to compile.

./src/App.vue
Module Error (from ./node_modules/eslint-loader/index.js):

C:\VueProjects\my-first-app\src\App.vue
  14:31  error  'username' is not defined  no-undef

✖ 1 problem (1 error, 0 warnings)

The error is a bit misleading because it tells us that username is not defined.

To reference a data property, we need to use this with dot notation to access the property.

Syntax: this
this.data_property

Let’s fix the example and change username in the function to this.username .

Example:
<template>
  <p>{{ greeting() }}</p>
</template>

<script>
export default {
  data() {
    return {
      username: 'John'
    };
  },
  methods: {
    greeting() {
      return 'Hello there ' + this.username
    }
  }
}
</script>

This time the project compiles and we see the greeting displays correctly in the browser.

At first its a bit confusing because we know that this refers to the calling object, so it shouldn’t work outside of the methods object.

It works because Vue creates a global application object behind the scenes, placing the data properties and methods in the same scope, and therefore of the same calling object.

Function parameters in Vue

Just like regular functions in Javascript, we can add parameters to functions in Vue and pass a parameter whenever we invoke the function.

Syntax:
<template>
  <p>{{ func(argument) }}</p>
</template>

<script>
export default {
  methods: {
    func(parameter) {
      // use parameter
    }
  }
}
</script>

As an example, let’s change our greeting function to have a parameter called name , then add a name as argument when we invoke the method in the template.

Example: src/App.vue
<template>
  <p>{{ greeting('Obi Blonde Kenobi') }}</p>
</template>

<script>
export default {
  methods: {
    greeting(name) {
      return 'Help us ' + name
    }
  }
}
</script>

If we run the example in the browser, we’ll see the personalized greeting.

Arrow functions in Vue

Although we can and will use arrow functions in Vue, there may be certain situations where we can run into some problems.

To demonstrate, let’s convert our greeting function from earlier into an arrow function.

Example: src/App.vue
<template>
  <p>{{ greeting('Obi-Wan Kenobi') }}</p>
</template>

<script>
export default {
  methods: {
    greeting: (name) => {
      return 'Help us ' + name
    }
  }
}
</script>

Our app will compile without issue and display the greeting as expected.

But if we access a data property with this , we’ll run into a problem.

Example: src/App.vue
<template>
  <p>{{ greeting() }}</p>
</template>

<script>
export default {
  data() {
    return {
      username: 'Obi-Wan Kenobi'
    };
  },
  methods: {
    greeting: () => {
      return 'Help us ' + this.username
    }
  }
}
</script>

The application still compiles, but nothing shows on the page in the browser. If we inspect the page in the browser’s dev tools and open the Console, we’ll see an error.

Output:
Uncaught TypeError: Cannot read property 'username' of undefined

That means that the this keyword is undefined. This relates to how Javascript works, not Vue specifically.

Earlier we mentioned that Vue creates a global object in the background that puts the data properties and methods in the same scope.

Let’s imagine that the methods option in the following example is the global object that Vue creates behind the scenes.

Example: src/App.vue
<template>
  <p>{{ thisInArrowFunc() }}</p>
  <p>{{ thisInRegularFunc() }}</p>
</template>

<script>
export default {
  methods: {
    username: 'Obi-Wan Kenobi',

    thisInArrowFunc: () => {
      return 'Help us ' + this.username // no 'this' binding
    },

    thisInRegularFunc() {
      return 'Help us ' + this.username // global 'this'
    }
  }
}
</script>

When we invoke a regular function, the value of this equals to the global object. So this has access to username and will work as expected.

An arrow function doesn’t have its own this . An arrow function inherits its this from the closest non-arrow parent function.

Because there is no outer non-arrow function, there is no this it can inherit from. Therefore the arrow function’s this is undefined and we get the error in the console.

A good rule to remember is to use regular functions as outer functions and arrow functions as inner functions, if there is a need. In most cases the code can be refactored in a way that removes the need for an arrow function.

Reference vs Invoke: function () { [native code] }

Some students encounter a situation where they want to display the result of a function on the page with string interpolation, but it displays function () { [native code] } instead.

That happens when we try to reference a function instead of invoking it.

  • We reference a function by excluding the parentheses at the end. Callback functions are referenced.
  • We invoke a function by adding parentheses at the end. Standard functions are invoked.

Let’s see an example where we reference a function.

Example: src/App.vue
<template>
  <p>{{ greeting }}</p>
</template>

<script>
export default {
  methods: {
    greeting() {
      return 'Hello there'
    }
  }
}
</script>

The project compiles successfully without errors but on the page it displays function () { [native code] } .

To fix the issue, simply add the parentheses to the function name to invoke it.

Example: src/App.vue
<template>
  <p>{{ greeting() }}</p>
</template>

<script>
export default {
  methods: {
    greeting() {
      return 'Hello there'
    }
  }
}
</script>

Now the page displays the message from the function as intended.

This typically happens because students read or hear somewhere that they don’t have to use the parentheses if the function doesn’t contain any parameters.

It’s true, but only in certain situations, like when the function is passed as a value to certain directives. In that case we technically send our function to a parent function that runs it behind the scenes (a callback) so it works.

We haven’t covered event handling yet, but let’s use it as an example.

Example: src/App.vue
<template>
  <p>{{ num }}</p>

  <button v-on:click="increment">Increase number</button>
  <button v-on:click="decrement">Decrease number</button>
</template>

<script>
export default {
  data() {
    return {
      num: 0
    }
  },
  methods: {
    increment() {
      this.num++
    },
    decrement() {
      this.num--
    }
  }
}
</script>

In the example above we use the v-on directive to handle a click event. The two methods define what should happen when the click event fires. They don’t have any parameters and their directive allows us to reference them without parentheses, so it works.

Computed properties are also referenced.

Example: src/App.vue
<template>
  <p>Your name: {{ fullName }}</p>

  <p>First name: <input type="text" @input="getFirstName"></p>
  <p>Last name:  <input type="text" @input="getLastName"></p>
</template>

<script>
export default {
  data() {
    return {
      firstName: '',
      lastName: ''
    };
  },
  methods: {
    getFirstName(event) {
      this.firstName = event.target.value
    },
    getLastName(event) {
      this.lastName = event.target.value
    }
  },
  computed: {
    fullName() {
      return this.firstName + ' ' + this.lastName
    }
  }
}
</script>

In the example above we use the fullName computed property to combine the first and last names from the text input fields. At the top of the template block, we reference it with string interpolation without parentheses.

Don’t worry if you don’t understand the two examples above right now, their concepts will become clear when we cover them. What’s important is that methods should be invoked with parentheses when using string interpolation.