使用助焊剂时的表单验证
Posted
技术标签:
【中文标题】使用助焊剂时的表单验证【英文标题】:Form validation when using flux 【发布时间】:2016-05-05 22:18:22 【问题描述】:我在我的应用程序中使用通量,我使用Backbone.View
作为视图层。
一般整个页面都有一个store实例,store保存应用的数据(或状态),视图会监听store
的change
事件,当store
触发change
事件,视图将相应地重新渲染。
到目前为止一切顺利,但是我在使用表单时遇到了一些问题,当使用尝试提交表单或为元素触发 blur
事件时,我想验证服务器中的输入并显示错误尽快,这就是我所做的:
当用户点击提交按钮或某个元素的值发生更改时,我将发送如下操作:
dispatch(type:"validate",value:"value");
store
将响应此操作并向服务器发送请求
当响应返回时,我将更新存储并触发change
事件:
store.validate_response=response;
store.trigger("change");
我可以显示错误但我不能保留元素的值,因为表单中的元素会重新渲染,这意味着它们将显示原始值而不是用户键入的值。
我曾想过在像这样发送验证操作时也保存输入的值:
dispatch(type:"validate",value:"value",userTypedValueForEveryElement:".....");
它在使用点击提交按钮时起作用,因为通常当他们点击按钮时,他们不会在表单中输入任何内容,但是这种情况如何:
<input type="text" id="A" />
<input type="text" id="B" />
用户在input
A
中输入avalue
,然后在input
B
中输入bv
,同时我会做验证,并在派发动作时发送两个值:
a:"avalue",b:"bv"
store
将保留这些值。
在请求过程中,用户不断输入元素B
,现在值是bvalue
,同时返回验证响应,然后表单将重新渲染,并设置avalue
对于A
和bv
对于B
,这就是重点,B
的值丢失了,用户会感到惊讶,他们不知道发生了什么。
有办法解决这个问题吗?
看来flux
的方式:
view trigger action -->
store respond to actions -->
store trigger changed -->
view respond to store(re-render in most case) -->
view trigger action"
使这种要求比以前复杂。一旦您的视图有太多的交互性,您将不得不做更多额外的工作来保持视图的状态。
这是真的还是因为我错过了什么?
【问题讨论】:
输入验证它自己而不去商店怎么样? @korven 这将打破flux/redux prencple,并且有很多交互,验证就是其中之一。 当我使用 Flux 设计时,我从不在存储中保留任何逻辑,只保留组件可以显示的值。逻辑放置在组件中。 Leet 输入验证它自己到服务器,如果正确发送操作来存储以保持新值 不要重新渲染整个表单,只更新改变的部分。 【参考方案1】:在编写我的 react 应用程序时,我遇到了完全相同的问题。结果,我最终编写了一个小型库来实现相同的目标。
https://www.npmjs.com/package/jsov
您需要做的就是这样,只要 store 使用他们键入的数据触发更改。您的组件中将有一个 onChange 函数,它将从 store 中侦听此更改(并可能设置状态),现在您在这里要做的是在设置状态之前使用
onChange:function()
var validated_response=JsOV.schemaValidator(Schema,Store.getResponse());
this.setState(data:validated_response);
P.S:为了避免痛苦,我还在库中提供了模式生成器函数。它接受一个虚拟响应并生成模式样板,您可以在其中添加自己的验证。
【讨论】:
【参考方案2】:听起来您在这里遇到了一些不同的问题,但它们都是可以解决的。这有点长,但希望它能解决您遇到的所有问题。
商店设计:首先,您的商店实际上要保存哪些信息?尽量不要像 Backbone 模型那样考虑 Flux 商店,因为它们的目的并不完全相同。 Flux 存储应该存储应用程序状态的一部分(不一定是 UI 组件状态的一部分),并且不应该知道或关心使用它的任何视图。牢记这一点可以帮助您将行为和数据放在正确的位置。因此,假设您的商店正在跟踪用户对特定表单的输入。由于您的应用程序关心输入是否有效,因此您需要以某种方式在商店中表示它。您可以将每个输入表示为存储中的一个对象,例如val: 'someInput', isValid: false
。无论您如何存储它,它都必须在那里;您应用的任何部分都应该能够从存储中提取数据并知道哪些输入有效/无效。
我同意@korven 的观点,即在 Store 中放置大量应用程序逻辑是一个糟糕的选择。我将 AJAX 调用放入动作创建逻辑中,AJAX 响应回调在 Dispatcher 上创建实际动作;我不止一次看到这个推荐。
保留用户输入: 一方面,您只想在用户完成输入后呈现表单输入 - 否则,渲染将在他们输入时更改文本。这很容易——throttle 或debounce(这里可能更适合去抖动)用户输入事件的输入验证处理程序。 (如果您使用焦点或模糊事件,时间不太可能成为问题,但您仍应考虑它。)仅在验证完成后更新商店。而且,当然,仅在商店更新时渲染。所以我们只有在用户停止输入并且我们已经验证了他们的输入时才修改 DOM 中的输入值。
即使使用限制/去抖动,由于验证请求是异步的,并且用户可以(可能)在短时间内触发许多验证请求,因此您不能依赖按顺序返回的响应。换句话说,您无法在每个响应返回时对其进行处理;如果它们乱序返回,您将用旧输入覆盖最近的输入。 (我在现实生活中遇到过这种情况。这对你来说可能是一个边缘情况,但当它发生时,这个错误会令人困惑,值得提前解决。)幸运的是,我们只关心 最近的 用户键入的内容。所以我们可以忽略对验证请求的所有响应,除了最近请求的响应。通过跟踪每个请求的“键”,您可以轻松地将此逻辑与发出请求的任何内容集成。这是我如何解决此问题的示例:
// something in your view
this.on(keyup, function()
var input = this.getUserInput();
validationService.validate(input);
// within validationService
validate: function(input)
// generate random int between 1 and 100
var randKey = Math.floor(Math.random() * (100 - 1)) + 1;
this.lastRequestKey = randKey;
this.doAjaxRequest(
data: input: input,
callback: function()
if (randKey !== this.lastRequestKey)
// a newer request has modified this.lastRequestKey
return;
// do something to update the Store
);
在此示例中,负责验证服务的对象仅“记住”最近为请求设置的“密钥”。由于其闭包,每个回调在范围内都有其原始键,并且它可以检查其原始键是否等于服务对象上设置的键。如果没有,则意味着发生了另一个请求,我们不再关心此响应。您需要为每个字段设置“键”,以便对字段 B 的新请求不会覆盖对字段 A 的旧请求。您可以通过其他方式解决此问题,但重点是,丢弃除任何给定输入的最后一个请求的响应。这还有一个额外的好处,就是为那些丢弃的响应节省了一些更新/渲染周期。
多字段渲染:当您正确使用 Flux 时,您永远不应该“丢失”数据,因为所有更改都来自 Dispatcher/Store,并且因为 Dispatcher 不会发送新的更新到商店,直到上一次更新完全完成。因此,只要您使用每个新输入更新 Store,您就不会丢失任何内容。您不必担心对输入 B 的更改会导致您丢失对正在进行的输入 A 的更改,因为对输入 A 的更改将从 Dispatcher 流向 Store 再到 View 并完成渲染 在 Dispatcher 之前将允许对输入 B 的更改开始处理。 (这意味着渲染应该很快,因为它们会阻塞下一个操作。React 使用 Flux 运行良好的原因之一。)
只要您将所有东西都放入存储中——并且不要将错误的东西放入存储中,即上面的输入和异步处理的东西——你的 UI 将是准确的。 Flux 模式将每次更改视为原子事务,保证在下一次更改发生之前完成。
【讨论】:
以上是关于使用助焊剂时的表单验证的主要内容,如果未能解决你的问题,请参考以下文章
使用 iPhone UIWebView 时的 Asp.Net 表单身份验证