Typically, inside methods, or computed properties or lifecycle handlers in Vue, you will use this
to refer the component to which the method/computed/handler is attached. this
refers to the context in which the function is currently executing.
Where you get into trouble using this
is when a new function is declared in the context of the current function, as happens when you write a callback to a promise (axios.post
, axios.get
). Consider this code:
AddTicket: function () {
// "this", on this line, refers to the Vue
// and you can safely use "this" to get any of the
// data properties of the Vue
axios.post('/api/Tickets', ...)
.then(function (response) {
// "this" HERE, does NOT refer to the Vue!!
// The reason why explained below
})
}
In the above code, the first comment could be replaced with code that uses this
to get data properties or call methods of the Vue (this.tickets
). The second comment, however is inside a new function context, and this
will NOT refer to the Vue. This is because in Javascript when you declare a new function using the function() {}
syntax, that function has its own function context which is different from the function in which it is declared.
There are several ways to deal with this in Javascript. The most common these days are to either use a closure to capture the correct this
, or to use an arrow function. Consider this code:
AddTicket: function () {
// As before, "this" here is the Vue
axios.post('/api/Tickets', ...)
.then((response) => {
// "this" HERE is ALSO the Vue
})
}
Note that in this example, the callback is defined using an arrow function (() => {}
). Arrow functions do NOT create their own function context and use the context in which they are declared. This is also known as having a lexical scope.
The other most common workaround is using a closure.
AddTicket: function () {
const self = this // Here we save a reference to the "this" we want
axios.post('/api/Tickets', ...)
.then(function(response){
// and HERE, even though the context has changed, and we can't use
// "this", we can use the reference we declared (self) which *is*
// pointing to the Vue
self.tickets = response
})
}
Finally, you can use the bind method to create a function with a specific this
, though this is not as common these days with arrow functions available.
AddTicket: function () {
axios.post('/api/Tickets', ...)
.then(function(response){
this.tickets = response
}.bind(this)) // NOTE the ".bind(this)" added to the end of the function here
}
In almost no case, should you really be doing what you do in your question, which is save a reference to the Vue in the variable vm
and use that variable inside the Vue object itself. That's a bad practice.
In any case, how to use the correct this
is covered in detail in numerous posts throughout the internet and here on StackOverflow as well.
Finally, here is the code from the question revised such that this
should be used correctly.
var vm = new Vue({
el: '#app',
data: {
tickets: [],
top: 100,
search: '',
showAdd: false,
ticket: null
},
mounted: function () {
// there is no need for $nextTick here
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;
}
},
})