Knockout.js:计算可选定义值的可观察量

时间:2013-11-24 05:37:56

标签: javascript knockout.js

我为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不会使其成为可观察对象。那么如何有条件地定义百分比表示呢?

1 个答案:

答案 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);

希望这有帮助!