جاوااسکریپت - تایپاسکریپت: TypeError گرفتهنشده: this.fnName یک تابع نیست
من با تایپاسکریپت کار میکنم و یک فراخوانی آژاکس دارم که در صورت موفقیت، یک تابع یا متد دیگر را صدا میزند.
deleteTheseSuccess(data) {
new Fe.Upsm.Head().showGlobalNotification("Selected Items Deleted");
this.refreshPrintQueueGrid(); <== this is there the error existst
(window as any).parent.refreshOperatorPrintQueueCount();
}
تماس متدی که در بالا به آن اشاره کردهام در حال تلاش برای فراخوانی است
refreshPrintQueueGrid() {
$("#PrintQueueGrid").data("kendoGrid").dataSource.read();
this.refreshPrintQueueStats();
}
این بدون هیچ مشکلی در VS2017 کامپایل میشود و فایلهای جاوااسکریپت را مطابق انتظار تولید میکند
و این تماس در خروجی جاوااسکریپت است
PrintQueue.prototype.refreshPrintQueueGrid = function () {
$("#PrintQueueGrid").data("kendoGrid").dataSource.read();
this.refreshPrintQueueStats();
};
خطای دقیقی که دریافت میکنم این است
خطای نوع بدون کنترل: this.refreshPrintQueueGrid یک تابع نیست.
من قدردان خواهم بود اگر کسی بتواند به من کمک کند تا بفهمم چه چیزی در اینجا اشتباه است و چه چیزی میتواند باعث این مسئله شود زیرا من این مشکل را در چندین قسمت از برنامه خود خواهم داشت.
--- ویرایش کد کامپایل شده
PrintQueue.prototype.refreshPrintQueueGrid = function () {
$("#PrintQueueGrid").data("kendoGrid").dataSource.read();
this.refreshPrintQueueStats();
}
--- ویرایش ۲ -- کلاس
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");
}
}
}
پاسخ شماره ۱
مشکل این است که در callback ایجکس شما
dataAccess.ajaxCall(this.deleteTheseError, this.deleteTheseSuccess);متد this.deleteTheseSuccess زمینه 'this' را از دست میدهد (چون توسط فراخوانی ajax فراخوانی شده است).
شما میتوانید این تابع را توسط تابع دیگری که this را به عنوان متغیر self شامل میشود، پیچیده کرده و آن را به dataAccess.ajaxCall منتقل کنید
ویرایش
چیزی شبیه به این امتحان کن (من کد را از ذهن مینویسم پس آن را تست کن):
آن را به کلاس PrintQueue با دنبال کردن روش اضافه کنید
public deleteTheseSuccessWrapper() {
let self = this;
return () => { self.deleteTheseSuccess(); };
}
و از آن در متد deleteThese استفاده کنید با:
dataAccess.ajaxCall(this.deleteTheseError, this.deleteTheseSuccessWrapper());
ویرایش - نسخه ۲
همانطور که @estus در نظرات اشاره میکند: چرا به deleteTheseSuccessWrapper نیاز است؟ ما میتوانیم سعی کنیم آن را فقط با استفاده از فلش چاق حذف کنیم:
dataAccess.ajaxCall(this.deleteTheseError, () => { this.deleteTheseSuccess() });
پس ما یک راهحل oneliner داریم - من کد بالا را از ابتدا نوشتم اما فکر میکنم باید کار کند
پاسخ شماره ۲
متدهای deleteTheseError و deleteTheseSuccess به عنوان callback ارسال میشوند. همانطور که در سوال مرتبط اصلی توضیح داده شده است، راههایی وجود دارد تا آنها را به زمینه مناسب this متصل کرد.
امکان دارد این متدها را به صورت توابع پیکانی تعریف کرد، اما همانطور که این پاسخ توضیح میدهد، روش عمومیتر این است که آنها را به عنوان متدهای نمونه تعریف کرده و در سازنده کلاس به آنها bind کرد:
constructor(callingView: string) {
this.deleteTheseError = this.deleteTheseError.bind(this);
this.deleteTheseSuccess = this.deleteTheseSuccess.bind(this);
...
}
علاوه بر روشهای arrow، تایپاسکریپت همچنین گزینهای برای استفاده از دکوریتورها در متدها ارائه میدهد، مثلاً از بسته bind-decorator. این یک نحو زیباتر برای .bind(this) فراهم میکند:
import bind from 'bind-decorator';
...
@bind
deleteTheseSuccess(data) {...}
@bind
deleteTheseError(xhr) {...}
...
