使用 Knockout Validation 进行表单验证

Form validation with Knockout Validation

我遇到了一些使用 Knockout 进行数据绑定的表单设计的架构问题。我有一个非常庞大的项目在工作,为了简单起见,假设我正在点击一个 API 端点(虽然不是 RESTful)并返回一个 JSON 响应。我目前 运行 通过自定义映射函数将所有属性转换为可观察对象。该表单工作正常,但我现在想使用 Knockout Validation 添加验证。我们还没有构建所有不同的规则,但我希望深入了解答案是通过我的可观察地图添加规则的最佳方式。另一个令人头疼的问题是,返回的响应具有根据服务器上的某些设置而变化的属性,这些设置可以在 UI 中更新,因此我不会为我可能需要的东西准备好可观察对象。我知道这非常令人困惑,所以也许一个模糊的、人为的例子会有所帮助:

前提条件:页面加载时启用设置 X

JSON 回复:

results: [
  { a: 1 },
  { b: 2 },
  { c: 3 }
]

处理中:

var arr = ko.observableArray([]);

$.get('/endpoint/').then(function(data) {
  data.results.forEach(function(result) {
    arr.push(mapToObservables(result));
  });
});

设置 Y 已启用,属性 "c" 不再相关但 "d" 相关,因此需要返回的 JSON 是:

updates: [
  { a: 1 },
  { b: 3 },(updated value)
  { d: 4 } (new value)
]

我最初观察属性 "a, b, c",所以 "a, b" 很好,因为它们的状态仍然是已知的。但是 属性 "d" 由于设置更改需要添加,我还没有在看它。

这个例子非常人为,但我不想有任何 IP 问题,所以希望它有意义。实际的应用程序要大得多,对同一端点的不同 GETs/POSTs 可能会导致 6 或 7 种不同的状态。检查 "is X set? Then set up Y's and Z's properties as empty observables with the appropriate rules, just in case" 似乎不太 scalable/maintainable(对于其他可能的情况也是如此)。

我真的希望这对可以指导我的人有一点意义。我进入这个项目时甚至不知道 Knockout 是什么,也不知道以前的应用程序是如何工作的,所以尝试吸收所有相关知识有点疯狂。提前谢谢大家!

编辑:

我接受了罗伊在下面的回答,因为它最接近我正在寻找的内容,但为了更清楚一点,请想象一下这种情况。我遇到了一个可以发回属性 abcd 的端点(但总是一个子集,而不是完整的集合)。如果我返回 ab,我可以轻松地使用 ko.mapping 让它们可见,以防它们发生变化。不过,我需要做的是允许用户基本上摆脱 b 并选择 c 来代替,例如。如果用户想要使用它们,我想不出一种干净的方法来在页面加载时为 cd 观察到 "set up" 以允许使用它们。唯一想到的是,我为我的应用程序中的所有可能字段设置了可观察对象,即使在 90% 的时间里,其中一半不会被使用。或者我想我可以使用像邮箱这样的 pubsub 系统在运行时动态创建所需的可观察对象。我知道使用 observables 有一些开销,但它们对我来说似乎相当轻量级,所以当同时考虑性能和开发时间时,我不确定这些方法中的哪一个 "best"。

这是您正在寻找的那种行为吗?

var d1 = {
  a: 1,
  b: 2
};
var d2 = {
  a: 'oo',
  c: 'ah'
};

var vm = {
  data:ko.observable()
  };
vm.data(ko.mapping.fromJS(d1));
setTimeout(function() {
  vm.data(ko.mapping.fromJS(d2));
}, 1000);


ko.applyBindings(vm);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.js"></script>
<div data-bind="if:data().a">A:<span data-bind="text:data().a"></span>
</div>
<div data-bind="if:data().b">B:<span data-bind="text:data().b"></span>
</div>
<div data-bind="if:data().c">C:<span data-bind="text:data().c"></span>
</div>