我为Yesod(一个Haskell Web框架)写了一个简洁的小knockout.js绑定,但是在处理可选的定义值时遇到了一些麻烦。绑定的Haskell部分旨在提供javascript片段,该片段对同一个处理程序进行Ajax调用,并接收由ko.mapping解析的JSON对象。还有一些用于定制的钩子。
这一切都很好,花花公子,但我在处理可选值时遇到了问题。如果我使用可选记录提供值,则JSON发射器会将其视为可选值,并且不会将其发出。
因此,例如,对
请求的JSON响应data Foo = Foo { bar :: Maybe Int
, baz :: Int
}
serveThis :: Foo
serveThis = Foo (Nothing) 0
是{baz:“0”}
我明白为什么会这样,我可以改变它(但如果可能的话,我宁愿不要)。问题是当我在serveThis的JSON表示上调用ko.mapping.fromJS时,bar字段不会变成observable。好的,我也明白了。我可以使用with binding来做条件数据绑定。但我不知道有足够的JavaScript来有条件地定义计算的observable。
我的真实代码如下:
var ViewModel = function (data) {
ko.mapping.fromJS(data, {}, this);
this.notes.stdDev.percent = ko.computed( function () {
return numeral( this.notes.stdDev() ).format("0%");
}, this);
}
因此,如果未在Haskell端定义标准偏差,则不会将其作为JSON字段发出,因此ko.mapping不会使其成为可观察对象。那么如何有条件地定义百分比表示呢?
答案 0 :(得分:2)
在省略stdDev
属性的情况下,您的视图模型是否也会省略该属性?或者,您的视图模型是否需要包含属性的关联observable,而不管初始值是什么?
如果是前者那么你只需要包含一些类似于以下的简单条件逻辑:
this.notes.stdDev.percent = ko.computed(function() {
return this.notes.stdDev ? this.notes.stdDev * 100 + "%' : 'n/a';
}, this);
这将检查是否存在stdDev
属性并返回静态值'n/a'
是否未定义属性。
但是,如果您的视图需要所有用户输入stdDev值,即使省略了初始值,我也不建议使用此模型。相反,您可能想要考虑放弃ko.mapping。我大约在一年前使用ko.mapping但是我最近一直在使用它,因为我使用JS模型获得了更好的结果。这是最近为我工作的视图模型架构的一个例子。
var viewModel = (function () {
return { init: init };
function init(data) {
var self = {
notes: ko.observable()
};
self.notes(new Notes(data));
ko.applyBindings(self);
return self;
}
function Notes(data) {
return {
stdDev: ko.observable(data.notes),
otherProp: ko.observable(data.otherProp),
}
}
}());
要注意的主要事项 - 至少在映射讨论的上下文中 - 是Notes
构造函数。虽然可能会复制从服务层返回的模型,但与映射解决方案相比,此模式允许可靠的数据结构。在确定它不适合所有情况之前,我与映射进行了长达6个月的斗争,这种明确的模型定义确实为您的视图模型增加了清晰度和可靠性。尽管映射可以简化管道代码,但它确实会对传入数据的结构产生不必要的依赖。事实仍然是,如果视图引用了视图模型中不存在的属性,那么视图将崩溃。
如果 reeeeeally 希望避免在两个层之间复制模型,那么可以使用折衷的解决方案来定义您所依赖的属性的默认值,并使用默认值扩展传入的数据模型他们失踪时的价值观。 jQuery的extend函数适用于此函数 - 如果您已经依赖于jQuery。举个例子:
var noteDefaults = {
stdDev: 0
};
var input = $.extend({}, defaults, data);
ko.mapping.fromJS(input, {}, this);
希望这有帮助!