没有最好的独特的全球方法,所以我认为答案是:“利用淘汰赛为这项工作提供的所有可能性中最好的”。
对于我正在使用的本地化,使用单独的视图模型,因为这是一个已经过测试的剪切粘贴解决方案,我可以在任何地方重用这个视图模型,里面没有特殊的功能或引用。
var LanguageViewModel = function () {
var self = this;
self.languages = ko.observableArray(languages);
self.selectedLanguage = ko.observable(languages[2]);
self.translation = ko.computed(function () {
return resources[self.selectedLanguage().id];
});
self.descriptionByCode = function (code) {
var translation = self.translation();
if (translation.hasOwnProperty(code)) {
var resource = translation[code];
if (resource.hasOwnProperty('name')) return resource.name
else return resource;
} else return '';
};
self.descriptionLinesByCode = function (code) {
var translation = self.translation();
if (translation.hasOwnProperty(code)) {
var resource = translation[code];
if (resource.hasOwnProperty('text')) return resource.text
else return [resource];
} else return [];
};
};
与许多其他常见的本地化模式一样,所有要翻译的东西都必须有一个代码,所以两个“核心函数”descriptionByCode() 和descriptionLinesByCode() 是一个常见的实现。
所以,我最终完成了以下实现:
1) 为通用翻译使用扩展器和可选的自定义绑定:
ko.extenders.translation = function (target, option) {
target.description = function () {
var code = target.peek();
return vm.language.descriptionByCode(code);
};
target.descriptionLines = function () {
var code = target.peek();
return vm.language.descriptionLinesByCode(code);
};
return target;
};
现在,我可以使用这样的数据绑定:
data-bind="text: observable.description()"
...但是通过使用像这样的简单自定义绑定:
ko.bindingHandlers.description = {
update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
var valueUnwrapped = ko.unwrap(valueAccessor());
ko.bindingHandlers.text.update(element, valueAccessor().description, allBindings, viewModel, bindingContext);
}
};
...我可以使用更简洁紧凑的语法:
data-bind="description: observable".
...也适用于可观察对象中的复杂对象(感谢 neilculver):
data-bind="description: 'subproperty'".
最后,我明白我不能跳过使用计算的 observables
全部,但是
- 我可以限制可观察属性的数量,因为我可以将一个可观察属性放入
对象或对象数组
- 我可以
通过嵌套不属于第一级的子可观察属性和计算的可观察对象来保持我的视图模型干净和小巧
他们的视图模型
作为概念证明,这里还有两个示例:
2) 使用剔除自定义函数,例如用于选定项目的本地化摘要:
ko.observableArray.fn.summary = function () {
return ko.computed(function () {
var items = this(),
total = 0,
descriptions = [];
for (var i = 0, l = items.length; i < l; i++) {
var item = items[i];
var quantity = ko.unwrap(item.quantity);
if (quantity > 0) {
descriptions.push(quantity + ' x ' + item.code.description());
total += quantity;
}
}
return descriptions;
}, this);
}
我可以这样做,因为我总是有一个数量和一个代码,我的视图模型保持非常简单和干净:
var OrdersViewModel = function (data) {
var self = this;
self.fruits = ko.observableArray(ko.utils.arrayMap(data, function (item) {
return new Fruit(item);
}));
self.fruits.orderSummary = self.fruits.summary();
};
3) 为其他人使用子可观察对象,例如用于淘汰选项绑定
当项目的选项也与我的数据一起提供时,我的项目视图模型将如下所示:
var items =
[{Code: "BA", Quantity: 1, UnitPrice: 0.35},
{Code: "AP", Quantity: 1, Types: ["RED", "GREEN", "YELLOW"], UnitPrice: 0.25},
{Code: "OR", Quantity: 0, UnitPrice: 0.45}];
function Fruit(data) {
var self = this;
self.code = ko.observable(data.Code).extend({
translation:null
});
self.code.unitPrice = data.UnitPrice;
self.code.typeOptions = data.Types;
self.type = ko.observable('');
self.type.options = ko.computed(function () {
var l = vm.language.selectedLanguage();
var typeOptions = self.code.typeOptions;
var items = ko.utils.arrayMap(typeOptions, function (item) {
var description = vm.language.descriptionByCode(item);
return {code: item, description: description};
});
return items;
});
self.quantity = ko.observable(data.Quantity);
self.totalPrice = ko.computed(function () {
return self.code.unitPrice * self.quantity();
});
}
...现在我可以为自动响应语言切换的本地化选择元素添加如下标记:
<select data-bind="options:item.type.options,optionsText:'description',optionsValue:'code',value:item.type"></select>
本地化选项数组的创建也可以从视图模型中提取并放入一个单独的函数中,该函数返回一个 ko.computed。
这是上述所有内容的工作小提琴:https://jsfiddle.net/ch9ubdu1/10/