ES6+Aurelia项目中,如何在detailInit内正确引用主上下文this?
解决Aurelia+ES6中Kendo回调的
this上下文绑定问题 我太懂这种迁移时的this上下文混乱了!从Knockout的var self = this切换到ES6+Aurelia,遇到Kendo UI这类第三方组件的回调时,确实容易踩坑——因为Kendo会把回调里的this绑定到它自己的组件实例,而不是你的ViewModel。
核心问题分析
你在detailInit里遇到的this.parent is undefined,本质是当Kendo调用detailInit时,函数内部的this已经变成了Kendo Grid的实例,而不是你的MenuItemViewModel实例,自然拿不到ViewModel上的parent属性。之前Knockout里的var self = this之所以有效,是因为你在ViewModel的方法里提前捕获了上下文,但detailInit被Kendo调用时,它内部的this早就不是ViewModel了,所以let self = this抓的是Kendo组件,根本没用。
两种可行的解决方案
方案1:在构造函数中手动绑定this
在ViewModel的构造函数里,把detailInit的this强制绑定到ViewModel实例,这样不管Kendo怎么调用它,this始终指向你的ViewModel:
export class MenuItemViewModel { constructor(parent) { this.parent = parent; // 关键:把detailInit的this绑定到当前ViewModel实例 this.detailInit = this.detailInit.bind(this); } init() { $("#items-grid").kendoGrid({ data: null, dataSource: { pageSize: this.parent.gridPageSize, // ...其他配置 }, dataBound: (e) => { // 这里用箭头函数代替self,直接访问ViewModel的this let body = $('#items-grid').find('tbody')[0]; if (body) { this.parent.templatingEngine.enhance({ element: body, bindingContext: this.parent }); } }, detailTemplate: kendo.template($("#items-template").html()), detailInit: this.detailInit // 现在这里的detailInit已经绑定了正确的this }); } detailInit(e) { // 现在this就是MenuItemViewModel的实例,可以正常访问this.parent var detailRow = e.detailRow; detailRow.find(".tabstrip").kendoTabStrip({ animation: { open: { effects: "fadeIn" } } }); detailRow.find(".detail-grid").kendoGrid({ data: null, dataSource: { pageSize: this.parent.gridPageSize, // 这里能正常拿到parent了 // ...其他配置 }, dataBound: (e) => { // 同样用箭头函数,避免this跑偏 let body = $(e.sender.element).find("tbody")[0]; if (body) { this.parent.templatingEngine.enhance({ element: body, bindingContext: this.parent }); } }, detailTemplate: kendo.template($("#items-template").html()), detailInit: this.detailInit // 递归调用也没问题,因为已经绑定了this }); } }
方案2:用ES6类字段+箭头函数定义detailInit
箭头函数不会创建自己的this,它会继承外层作用域的this(也就是ViewModel实例)。你可以直接把detailInit定义成箭头函数类型的类字段:
export class MenuItemViewModel { constructor(parent) { this.parent = parent; } init() { $("#items-grid").kendoGrid({ // ...和方案1一样的配置 detailInit: this.detailInit }); } // 用箭头函数定义detailInit,this始终指向ViewModel实例 detailInit = (e) => { var detailRow = e.detailRow; detailRow.find(".tabstrip").kendoTabStrip({ animation: { open: { effects: "fadeIn" } } }); detailRow.find(".detail-grid").kendoGrid({ data: null, dataSource: { pageSize: this.parent.gridPageSize, // 正常访问parent // ...其他配置 }, dataBound: (e) => { let body = $(e.sender.element).find("tbody")[0]; if (body) { this.parent.templatingEngine.enhance({ element: body, bindingContext: this.parent }); } }, detailTemplate: kendo.template($("#items-template").html()), detailInit: this.detailInit }); } }
额外提示
- 对于其他Kendo回调(比如
dataBound),也建议用箭头函数或者bind来确保this指向ViewModel,避免再出现类似问题。 - 类字段的箭头函数写法需要你的构建环境支持ES6+(或者用Babel转译),如果是老项目,方案1的
bind方式兼容性更好。
内容的提问来源于stack exchange,提问作者Matt
相关产品推荐
相关产品推荐

