Knockout JS如何映射嵌套字典数据并实现data-bind绑定
Knockout.js 根层级键值无法正常展示问题处理
问题背景
- 基于Knockout.js(KO JS)开发前端页面时,遇到数据根层级动态键值无法正常渲染展示的问题。此前曾针对同类嵌套数据绑定问题做过排查调试,虽取得一定进展但未彻底解决。
- 经排查确认,问题根源是后端返回的数据采用动态键嵌套格式,无法直接通过KO常规绑定语法读取,需要先对返回数据做映射转换。
原始数据与转换目标
后端返回的定价数据原始结构如下(对应结构可参考配套截图):
{ "Pricings": { "2021-22": { "Tbilisi": { "PriceHeaders": [], "Comment": "TBi", "Pricings": [{}] } }, "2020-21": { "Tbilisi": { "PriceHeaders": [], "Comment": "TBi", "Pricings": [{}] } } } }
原始结构以年份(如2020-21)作为动态根键,下属直接嵌套城市名作为二级键,无法直接用foreach绑定遍历,单组数据结构如下:
{ "2020-21": { "Tbilisi": {"PriceHeaders": Array(9), "Comment": "TBi", "Pricings": Array(1)} } }
需要将结构转换为固定字段的格式,转换后即可通过简单的foreach绑定直接读取Year字段值,以及Cities节点下的任意键值,目标格式如下:
{ "Year": "2020-21", "Cities": { "Tbilisi": {"PriceHeaders": Array(9), "Comment": "TBi", "Pricings": Array(1)} } }
已完成的转换逻辑
目前已经编写完成原始数据转目标结构的映射代码:
Object.entries(data).map(([year, cities]) => ( { "year": year, "cities": Object.entries(cities).map(([name, cities]) => ( { name, ...cities } )) } ))
当前卡点
转换逻辑编写完成后,无法将该逻辑正确接入KO的data-bind绑定语法中,现有绑定写法如下,无法正常读取转换后的结果供视图渲染:
<div data-bind="data : THIS FUNCTION HERE , as 'result'"></div>
解决方法
Knockout的绑定表达式仅支持简单的字段读取和基础运算,不支持直接嵌入复杂的对象转换逻辑,不要把转换代码直接写在data-bind属性里,按以下步骤处理即可:
- 把数据转换逻辑放到ViewModel层实现,在拿到后端返回的原始数据后第一时间做格式转换,将转换后的结果存为ViewModel上的计算属性(或普通可观察属性),供视图绑定直接调用。
参考ViewModel实现代码:function PricingViewModel() { const self = this; // 存储后端返回的原始数据 self.rawPricingData = ko.observable({}); // 提前转换好的格式化数据,直接供视图绑定使用 self.formattedPricingList = ko.computed(() => { const pricingGroup = self.rawPricingData().Pricings || {}; // 直接复用已写好的转换逻辑,注意修正内层参数重名问题 return Object.entries(pricingGroup).map(([year, cityMap]) => ({ year: year, cities: Object.entries(cityMap).map(([cityName, cityDetail]) => ({ cityName, ...cityDetail })) })); }); // 接口请求拿到后端数据后,给原始数据赋值即可自动触发格式化计算 fetch("你的定价数据接口地址") .then(res => res.json()) .then(resData => { self.rawPricingData(resData); }); } // 启动KO绑定 ko.applyBindings(new PricingViewModel()); - 视图层直接绑定已经转换完成的
formattedPricingList字段即可,不需要在绑定属性中写任何转换逻辑:<!-- 遍历转换后的年份维度数据 --> <div data-bind="foreach: formattedPricingList"> <div class="year-group"> <h3 data-bind="text: year"></h3> <!-- 遍历对应年份下的城市维度数据 --> <div data-bind="foreach: cities" class="city-item"> <p data-bind="text: cityName"></p> <p data-bind="text: Comment"></p> <!-- PriceHeaders、Pricings等其余字段按需绑定即可 --> </div> </div> </div>
注意:原有转换代码存在内层参数命名重复问题:外层解构出的
cities是年份对应的城市集合,内层map的第二个参数是单个城市的详情数据,二者重名会导致变量覆盖,容易引发取值异常,上述示例代码已经修正该问题。
内容的提问来源于stack exchange,提问作者Greg
相关产品推荐
相关产品推荐

