KnockoutJS中JSON数据无法映射至ViewModel的问题求助
我来帮你捋捋问题出在哪,以及怎么解决~
核心问题:异步操作的时机错配
你现在的代码里,PersonViewModel初始化的时候,Ajax请求还没完成——categories数组此时是空的,所以self.shoppingCart = ko.observableArray(categories)其实是把空数组赋值给了购物车的可观察数组。之后Ajax成功往categories里push数据,但Knockout的observableArray不会自动追踪原数组的变化:它只是在初始化时复制了原数组的内容,原数组后续的修改不会同步到可观察数组里,这就导致购物车始终显示空数据。
另外还有两个小细节需要修正:
Product构造函数里对productTags重复赋值了,先赋值普通数组又覆盖成observableArray,虽然不影响功能,但属于冗余代码。- HTML里绑定
productTags时用text: $data,会直接输出对象的字符串形式(比如[object Object]),应该绑定标签的Name属性才能显示正确内容。
具体修改方案
1. 重构JS代码:把数据加载逻辑移入ViewModel
不要用全局的categories数组,直接在ViewModel内部管理shoppingCart,Ajax成功后直接操作可观察数组,确保数据变化能被Knockout追踪到:
function Product(id, name, price, productTags) { var self = this; self.id = ko.observable(id); self.name = ko.observable(name); self.price = ko.observable(price); // 直接初始化observableArray,避免重复赋值 self.productTags = ko.observableArray(productTags || []); } function PersonViewModel() { var self = this; self.firstName = ko.observable("John"); self.lastName = ko.observable("Smith"); self.shoppingCart = ko.observableArray([]); // 初始化为空数组 self.checkout = function () { alert("Trying to checkout"); }; // 把Ajax请求移到ViewModel内部,直接操作shoppingCart $.ajax({ url: 'ShoppingCart/GetAllProducts', cache: false, type: 'GET', contentType: 'application/json; charset=utf-8', data: {}, success: function (data) { var parsed = JSON.parse(data); var products = parsed.Data; console.log(parsed); // 批量转换为Product实例 var mappedProducts = products.map(function(product) { return new Product(product.Id, product.Name, product.Price, product.ProductTags); }); // 直接替换shoppingCart的内容(也可以用forEach逐个push) self.shoppingCart(mappedProducts); } }); }; var viewModel = new PersonViewModel(); ko.applyBindings(viewModel, document.getElementById('shoppingCart'));
2. 修正HTML的标签绑定
把<li data-bind="text:$data">改成<li data-bind="text: Name">,才能正确显示标签名称:
<div id="shoppingCart"> <table> <thead><tr> <th>Item number</th> <th>Product</th> <th>Price</th> <th>Tags</th> <th>Action</th> </tr></thead> <tbody data-bind='foreach: shoppingCart'> <tr> <td data-bind='text: $index'></td> <td data-bind='text: name'></td> <td data-bind='text: price'></td> <td> <ul data-bind="foreach: productTags"> <li data-bind="text: Name"></li> </ul> </td> <td> <button data-bind='click: $root.removeProduct'>Remove</button> </td> </tr> </tbody> </table> </div>
额外补充:关于ko.mapping的用法
你提到ko.mapper无效,大概率是用法不对。如果想用自动映射简化代码,可以这样写:
function PersonViewModel() { var self = this; self.firstName = ko.observable("John"); self.lastName = ko.observable("Smith"); self.shoppingCart = ko.observableArray([]); self.checkout = function () { alert("Trying to checkout"); }; $.ajax({ url: 'ShoppingCart/GetAllProducts', cache: false, type: 'GET', contentType: 'application/json; charset=utf-8', data: {}, success: function (data) { var parsed = JSON.parse(data); // 用ko.mapping自动把JSON转为带observable的对象 var mappedProducts = ko.mapping.fromJS(parsed.Data); self.shoppingCart(mappedProducts()); } }); };
这种方式会自动把所有属性转成observable,注意HTML绑定要和JSON里的属性大小写一致(比如Name还是name)。
内容的提问来源于stack exchange,提问作者Whole Codes
相关产品推荐
相关产品推荐

