Usually, when working inside methods, computed properties, or lifecycle handlers in Vue, you will utilize the keyword this
to refer back to the component where the method/computed property/handler is linked. The context of this
is determined by the current execution of the function.
The issue arises with using this
when a new function is defined within the scope of the current function, which occurs when creating a callback for a promise (e.g., axios.post
, axios.get
). Take a look at this code snippet:
AddTicket: function () {
// On this line, "this" points to the Vue instance
// It's safe to use "this" to access any data properties of the Vue
axios.post('/api/Tickets', ...)
.then(function (response) {
// However, "this" HERE does NOT refer to the Vue!!
// The explanation for this behavior is provided below
})
}
In the above scenario, the first comment could be replaced with code that utilizes this
to access data properties or invoke methods of the Vue (this.tickets
). Nonetheless, the second comment is situated within a new function context, and thus, this
will not reference the Vue object. This is due to the fact that when declaring a new function in JavaScript using the function() {}
syntax, that function possesses its own distinct function context separate from the enclosing function.
There are various ways to tackle this issue in JavaScript. One common approach nowadays is to either employ a closure to capture the correct this
or use an arrow function. Consider this revised code:
AddTicket: function () {
axios.post('/api/Tickets', ...)
.then((response) => {
// In this case, "this" ALSO refers to the Vue instance
})
}
It's worth noting that in this example, the callback is defined using an arrow function (() => {}
). Arrow functions do not establish their own function context but instead rely on the context in which they are declared—referred to as having lexical scope.
Another commonly used workaround is employing a closure.
AddTicket: function () {
const self = this // Saving a reference to the desired "this"
axios.post('/api/Tickets', ...)
.then(function(response){
// Despite the change in context here, and being unable to utilize
// "this", we can leverage the previously declared reference (self)
// pointing to the Vue instance appropriately
self.tickets = response
})
}
Lastly, one can utilize the bind method to create a function with a specific this
. Nevertheless, this practice is less prevalent nowadays with the availability of arrow functions.
AddTicket: function () {
axios.post('/api/Tickets', ...)
.then(function(response){
this.tickets = response
}.bind(this)) // Include ".bind(this)" at the end of the function here
}
Under most circumstances, it's advisable not to follow the pattern demonstrated in your question where a reference to the Vue instance is stored in the variable vm
and utilized within the Vue object itself. Such a practice is considered undesirable.
Furthermore, detailed information on correctly handling this
can be found in numerous tutorials spread across the internet and discussions on platforms like StackOverflow.
Finally, here's the modified code from your initial query ensuring proper use of this
:
var vm = new Vue({
el: '#app',
data: {
tickets: [],
top: 100,
search: '',
showAdd: false,
ticket: null
},
mounted: function () {
// Omitting $nextTick since unnecessary
this.GetTickets(100)
},
methods: {
GetTickets: function (top) {
axios.get('/api/Tickets', { params: { Top: top }})
.then(response => this.tickets = response.data)
.catch(error => console.log(error));
},
ClearTicket: function () {
var t = {
"ticketSubject": '',
"contactName": '',
"createdAt": moment()
}
this.ticket = t;
this.showAdd = !this.showAdd;
},
AddTicket: function () {
axios.post('/api/Tickets', this.ticket)
.then(() => this.GetTickets(100))
.catch(error => console.log(error));
this.showAdd = false;
}
},
})