Knockout映射嵌套对象为Observable时购物车加载选项不显示修复
嘿,我仔细看了你的问题,核心症结就是对象引用不匹配:你保存购物车时把cartItemName的完整JSON存了起来,加载时又用ko.mapping.fromJS重新创建了一个全新的Observable对象,但下拉框绑定的选项是原来availableItems数组里的对象——新对象和旧对象虽然数据一样,但不是同一个引用,Knockout的下拉框绑定是通过对象引用来匹配选中项的,自然就识别不出来了。
下面是具体的修复方案:
1. 修改保存逻辑:只保存唯一标识而非完整对象
保存购物车时,不要存cartItemName的整个对象,只存它的id(或者其他唯一标识),这样加载时我们可以通过这个id找到原选项对象:
self.saveCart = function() { // 先把购物车数据转成普通JS对象 var plainCart = ko.mapping.toJS(self.cartItems); // 把每个购物项的cartItemName替换为对应的id var cartToSave = plainCart.map(item => ({ ...item, cartItemName: item.cartItemName.id })); localStorage.setItem('savedCart', JSON.stringify(cartToSave)); };
2. 修改加载与映射逻辑:通过id匹配原选项对象
加载时,我们从本地存储拿到id后,去availableItems数组里找到对应的原对象,再把它赋值给购物项的cartItemName,而不是重新创建新对象:
首先调整CartItem构造函数,直接将传入的原对象包装成Observable:
function CartItem(data) { var self = this; // 直接用传入的原选项对象创建Observable self.cartItemName = ko.observable(data.cartItemName); self.quantity = ko.observable(data.quantity); }
然后修改Mapping配置的create回调,处理购物项时根据id匹配原对象:
var mapping = { 'cartItems': { create: function(options) { // 根据保存的id找到availableItems里的原对象 var matchedItem = self.availableItems.find(item => item.id === options.data.cartItemName); // 用原对象创建CartItem实例 return new CartItem({ cartItemName: matchedItem, quantity: options.data.quantity }); } } };
最后保持loadCart函数的逻辑不变,现在它读取到的cartItemName是id,会被上面的回调正确处理:
self.loadCart = function() { var savedCart = localStorage.getItem('savedCart'); if (savedCart) { ko.mapping.fromJS(JSON.parse(savedCart), mapping, self.cartItems); } };
为什么这样能解决问题?
Knockout的下拉框绑定(比如options和value绑定)是通过对象引用来判断选中状态的。现在加载后的cartItemName引用的是availableItems数组里的原对象,和下拉选项的引用完全一致,Knockout就能正确识别并显示选中的选项了。
你可以测试一下:添加商品到购物车,保存后刷新页面,点击加载购物车,下拉框应该能正常显示之前选中的商品选项了。
内容的提问来源于stack exchange,提问作者OfirD

