在Magento 2中用Knockout.js实现选中配送方式的信息展示
问题:Magento 2中用Knockout.js实现选中配送方式的动态汇总展示
我是Knockout.js新手,觉得它上手难度较高,之前用jQuery轻松实现的功能在Knockout.js里没法直接照搬。我在Magento 2店铺里,想实现选中配送方式后,在汇总框里动态展示该配送方式的名称、时间范围、价格等信息。
目前Magento已经用checked绑定把选中的配送方式同步到了quote中,每个配送方式的输入元素还带有自定义数据(比如配送名称、时间范围),我想要的汇总展示效果如下:
<div id="selected-method-summary"> <span class="method-name" data-bind="text: selectedMethod.name"></span> <span class="method-timerange" data-bind="text: selectedMethod.timerange"></span> <span class="method-price" data-bind="text: selectedMethod.price"></span> </div> <fieldset class="fieldset rate" data-bind="visible: (rates().length > 0)"> <dl class="items methods" data-bind="foreach: shippingRateGroups, mageInit: {'accordion':{'openedState': 'active', 'collapsible': true, 'active': [2], 'multipleCollapsible': true}} "> <div class="method-group" attr="'id': $data" data-role="collapsible"> <dt class="item-title" data-role="title"><span data-bind="text: $data"></span> <span class="accordion-toggle"></span></dt> <dd class="item-options" data-role="content" attr="'id': $data + 'content'" data-bind="foreach: { data:$parent.getRatesForGroup($data), as: 'method' }"> <div click="element.selectShippingMethod" attr="'data-date': method.extension_attributes.date, 'data-timerange': method.extension_attributes.time_range" data-bind="css: {'field choice item': available, 'message error': !available} "> <!-- ko ifnot: (available) --> <div data-bind="text: error_message"></div> <!-- /ko --> <!-- ko if: (available) --> <input type="radio" class="radio" data-bind=" click: $parents[1].selectShippingMethod, checked: element.isSelected, attr: { value: carrier_code + '_' + method_code, id: 's_method_' + carrier_code + '_' + method_code } "/> <label class="label" data-bind="attr: {for: 's_method_' + carrier_code + '_' + method_code}"> <!-- ko text: $data.method_title --><!-- /ko --> <each args="element.getRegion('price')" render="" /> </label> <!-- /ko --> </div> </dd> </div> </dl> </fieldset>
现在的问题是,data-bind="checked: element.isSelected"只返回一个拼接的carrier_code_method_code值,对应的计算属性代码如下:
isSelected: ko.computed(function () { return quote.shippingMethod() ? quote.shippingMethod()['carrier_code'] + '_' + quote.shippingMethod()['method_code'] : null; }),
我想扩展这个功能,让汇总框能动态展示选中配送方式的详细数据,但又怕修改原有代码会破坏Magento的默认功能,请问该怎么实现?
解决方案
1. 新增selectedMethod可观察对象,不修改原有isSelected
不要直接修改Magento自带的isSelected计算属性,而是在对应的ViewModel里新增一个selectedMethod可观察对象,用来存储选中配送方式的完整数据:
// 在ViewModel的初始化函数里添加 this.selectedMethod = ko.observable(null); // 监听quote.shippingMethod的变化,同步更新selectedMethod var self = this; quote.shippingMethod.subscribe(function(newShippingMethod) { if (newShippingMethod) { // 从所有配送方式中找到匹配的完整数据 var matchedMethod = null; // 遍历所有配送组 ko.utils.arrayForEach(self.shippingRateGroups(), function(group) { var rates = self.getRatesForGroup(group); matchedMethod = ko.utils.arrayFirst(rates, function(rate) { return rate.carrier_code + '_' + rate.method_code === newShippingMethod.carrier_code + '_' + newShippingMethod.method_code; }); if (matchedMethod) return; }); if (matchedMethod) { // 组装需要展示的数据,包含自定义属性 self.selectedMethod({ name: matchedMethod.method_title, timerange: matchedMethod.extension_attributes?.time_range || '', price: matchedMethod.price_formatted || '' // 这里用Magento格式化后的价格 }); } else { self.selectedMethod(null); } } else { self.selectedMethod(null); } });
2. 优化汇总框的绑定逻辑
为避免selectedMethod为空时出现报错,给汇总框添加if绑定,并调整内部数据绑定的写法:
<div id="selected-method-summary" data-bind="if: selectedMethod()"> <span class="method-name" data-bind="text: selectedMethod().name"></span> <span class="method-timerange" data-bind="text: selectedMethod().timerange"></span> <span class="method-price" data-bind="text: selectedMethod().price"></span> </div>
3. 用Magento Mixins扩展ViewModel(不修改核心代码)
在Magento 2中,不要直接修改核心模块的ViewModel文件,应该用mixins来扩展对应的ViewModel(比如Magento_Checkout/js/view/shipping):
- 创建自定义模块,在
requirejs-config.js中配置mixin:
var config = { config: { mixins: { 'Magento_Checkout/js/view/shipping': { 'Your_Module/js/view/shipping-mixin': true } } } };
- 编写
shipping-mixin.js文件:
define([ 'ko', 'Magento_Checkout/js/model/quote' ], function(ko, quote) { 'use strict'; return function (shippingViewModel) { // 新增selectedMethod可观察对象 shippingViewModel.selectedMethod = ko.observable(null); // 监听quote.shippingMethod变化 quote.shippingMethod.subscribe(function(newShippingMethod) { if (newShippingMethod) { var matchedMethod = null; ko.utils.arrayForEach(shippingViewModel.shippingRateGroups(), function(group) { var rates = shippingViewModel.getRatesForGroup(group); matchedMethod = ko.utils.arrayFirst(rates, function(rate) { return rate.carrier_code + '_' + rate.method_code === newShippingMethod.carrier_code + '_' + newShippingMethod.method_code; }); if (matchedMethod) return; }); if (matchedMethod) { shippingViewModel.selectedMethod({ name: matchedMethod.method_title, timerange: matchedMethod.extension_attributes?.time_range || '', price: matchedMethod.price_formatted || '' }); } else { shippingViewModel.selectedMethod(null); } } else { shippingViewModel.selectedMethod(null); } }); // 初始化时触发一次,确保已有选中项时能显示数据 if (quote.shippingMethod()) { quote.shippingMethod.notifySubscribers(quote.shippingMethod()); } return shippingViewModel; }; });
这种方式完全不会修改Magento的核心代码,既实现了需求,又保证了系统的可维护性和兼容性。
内容的提问来源于stack exchange,提问作者schnacks
相关产品推荐
相关产品推荐

