Reputation: 3261
I am working with Typescript and have an ajax call that on Success is calling another function \ method.
deleteTheseSuccess(data) {
new Fe.Upsm.Head().showGlobalNotification("Selected Items Deleted");
this.refreshPrintQueueGrid(); <== this is there the error existst
(window as any).parent.refreshOperatorPrintQueueCount();
}
the method call that I have pointed out above is trying to call
refreshPrintQueueGrid() {
$("#PrintQueueGrid").data("kendoGrid").dataSource.read();
this.refreshPrintQueueStats();
}
This compiles without any issues in VS2017 and produces the JavaScript files accordingly
and this is the call in the JavaScript output
PrintQueue.prototype.refreshPrintQueueGrid = function () {
$("#PrintQueueGrid").data("kendoGrid").dataSource.read();
this.refreshPrintQueueStats();
};
The exact error that I get is
Uncaught TypeError: this.refreshPrintQueueGrid is not a function.
I would be grateful if anyone can help me understand what is going wrong here and what would cause this as I will have this in a number of places over my application.
--- Edit compiled code
PrintQueue.prototype.refreshPrintQueueGrid = function () {
$("#PrintQueueGrid").data("kendoGrid").dataSource.read();
this.refreshPrintQueueStats();
}
--- Edit 2 -- Class
namespace Fe.Upsm {
export class PrintQueue {
callingView: string
constructor(callingView: string) {
this.callingView = callingView;
this.documentReadyObjects();
}
refreshPrintQueueGrid() {
$("#PrintQueueGrid").data("kendoGrid").dataSource.read();
this.refreshPrintQueueStats();
}
deleteThese(ids) {
var jsonObj = {
ids: ids
}
var dataAccess = new DataAccess.AjaxDataAccessLayer(Fe.Upsm.Enums.AjaxCallType.Post,
Fe.Upsm.Enums.AjaxDataType.json,
"../../PrintQueue/DeletePrintQueueItems",
jsonObj);
dataAccess.ajaxCall(this.deleteTheseError, this.deleteTheseSuccess);
}
deleteTheseSuccess(data) {
new Fe.Upsm.Head().showGlobalNotification("Selected Items Deleted");
this.refreshPrintQueueGrid;
(window as any).parent.refreshOperatorPrintQueueCount();
}
deleteTheseError(xhr) {
alert("An Error Occurred. Failed to delete print queue items");
}
}
}
Upvotes: 2
Views: 4853
Reputation: 92537
The problem is that in your ajax callback
dataAccess.ajaxCall(this.deleteTheseError, this.deleteTheseSuccess);
method this.deleteTheseSuccess
loose 'this' context (because ajax call it).
You can wrap this function by other that contains this
as self
variable and pass it to dataAccess.ajaxCall
EDIT
Try something like this (i write code from head so test it):
Add it to class PrintQueue
following method
public deleteTheseSuccessWrapper() {
let self = this;
return () => { self.deleteTheseSuccess(); };
}
And use it in deleteThese
method by:
dataAccess.ajaxCall(this.deleteTheseError, this.deleteTheseSuccessWrapper());
EDIT - version 2
as @estus point out in comments: Why is deleteTheseSuccessWrapper needed? we can try to omit it just by using fat-arrow:
dataAccess.ajaxCall(this.deleteTheseError, () => { this.deleteTheseSuccess() });
So we have oneliner solution - I write above code from head but I think it should also work
Upvotes: 2
Reputation: 222760
deleteTheseError
and deleteTheseSuccess
methods are passed as callbacks. As explained in related umbrella question, there are ways to bind them to proper this
context.
It's possible to define these methods as arrow functions, but as this answer explains, a more universal way is to define them as prototype methods and bind them in class constructor:
constructor(callingView: string) {
this.deleteTheseError = this.deleteTheseError.bind(this);
this.deleteTheseSuccess = this.deleteTheseSuccess.bind(this);
...
}
Besides arrow methods, TypeScript also offers an option to use method decorators, e.g. from bind-decorator
package. This provides a nicer syntax for .bind(this)
:
import bind from 'bind-decorator';
...
@bind
deleteTheseSuccess(data) {...}
@bind
deleteTheseError(xhr) {...}
...
Upvotes: 2