Knockout.js foreach拼接location.href出现[object Object]问题
问题根因分析
实现异常主要由两个Knockout使用层面的问题导致:
- 冗余且错误的
ko.mapping.fromJS使用方式:你先手动实例化ViewModel构造函数生成jsonModel,又把这个带自定义方法的实例整体传给mapping插件生成新的绑定对象。这种写法完全冗余——mapping插件的设计初衷就是直接把原始JSON/JS对象转换成带observable属性的可绑定结构,不需要先手动实例化构造函数再二次包装。更严重的是,这种写法会导致方法上下文和数据结构错位:最终用来绑定的是mapping生成的新对象,但detailPage方法是从旧的jsonModel实例上复制来的,方法识别的资产数据结构和实际绑定到页面的foreach上下文结构不匹配,最终传参时触发隐式类型转换异常。 - 未正确取到observable属性的原始值:经过mapping插件处理后,
assets数组内每个对象的所有属性(包括Id)都会被自动包装成observable函数,并非普通的原始值。你在跳转方法里直接用asset.Id做字符串拼接,拿到的是observable函数本身而非存储的Id值,最终被隐式转换为[object Object]。
修正实现方案
核心调整点
- 去掉冗余的ViewModel实例化流程,直接用mapping插件转换后端传来的原始Model数据,避免数据结构错位。
- 将自定义跳转方法手动挂载到mapping生成的根绑定对象上,确保foreach上下文的
$parent能正确访问到方法。 - 取值时对observable类型的Id属性加括号调用,获取真实的Id值用于URL拼接。
可直接复用的完整代码
页面HTML部分(原有绑定逻辑无需改动)
<table class="table"> <thead> <tr> <th>Name</th> <th>Created By</th> </tr> </thead> <tbody data-bind="foreach: assets"> <tr class="assets" data-bind="click: $parent.detailPage"> <td> <span data-bind="text: FileName"></span> </td> <td> <span data-bind="text: CreatedBy"></span> </td> </tr> </tbody> </table>
脚本部分(修正后)
<script> // 直接将后端传入的原始Model数据通过mapping转换为带observable属性的根视图对象 const rootViewModel = ko.mapping.fromJS(@Html.Raw(Json.Encode(Model))); // 手动将跳转方法挂载到根对象上,确保foreach上下文的$parent能正确访问 rootViewModel.detailPage = function (currentAsset) { // currentAsset是foreach遍历的当前行对象,其Id为observable,加()获取真实值 const assetId = currentAsset.Id(); location.href = '@Url.Action("Details", "Assets")/' + assetId; }; ko.applyBindings(rootViewModel); </script>
可选:保留手写ViewModel构造函数的写法
如果你希望保留原有构造函数的结构,不需要完全依赖mapping插件的全量自动转换,可调整为如下写法,同样能正常运行:
<script> function ViewModel(assets) { var self = this; // 仅对传入的assets数组做observable转换,不要全量包装整个ViewModel self.assets = ko.mapping.fromJS(assets); self.detailPage = function (currentAsset) { location.href = '@Url.Action("Details", "Assets")/' + currentAsset.Id(); }; } // 直接实例化视图模型,传入后端Model数据即可,不要二次调用mapping var viewModel = new ViewModel(@Html.Raw(Json.Encode(Model))); ko.applyBindings(viewModel); </script>
注意:所有被mapping插件处理过的属性,不管嵌套在哪一层,都必须通过
属性名()的方式调用才能拿到存储的原始值,直接引用属性名拿到的是observable函数本身,不能直接用于业务逻辑计算或字符串拼接。
内容的提问来源于stack exchange,提问作者jean-francois carlier
相关产品推荐
相关产品推荐

