我正在尝试学习angularjs,目前正在查看表单和表单验证。通过跟踪角度站点上的文档,我有一个模板html文件,如下所示:
<form role="form" name="fnolForm" novalidate autocomplete="off" data-ng-submit="submit()">
<fieldset class="form-group">
<legend>Information about you</legend>
<label data-ng-class="{'has-error' : fnolForm.submitError && fnolForm.name.$invalid}">
Your name
<input class="form-control" type="text" data-ng-model="fnol.insuredParty.name" name="name" required/>
<span class="error" data-ng-show="fnolForm.submitError && fnolForm.name.$error.required">Please provide your name</span>
</label>
<label data-ng-class="{'has-error' : fnolForm.submitError && fnolForm.phoneNumber.$invalid}">
Contact number
<input class="form-control" type="tel" data-ng-model="fnol.insuredParty.phoneNumber" name="phoneNumber" required/>
<span class="error" data-ng-show="fnolForm.submitError && fnolForm.phoneNumber.$error.required">Please provide your contact number</span>
</label>
<label data-ng-class="{'has-error' : fnolForm.email.$error.email || (fnolForm.submitError && fnolForm.email.$invalid)}">
Email address
<input class="form-control" type="email" data-ng-model="fnol.insuredParty.email" name="email" required/>
<span class="error" data-ng-show="fnolForm.submitError && fnolForm.email.$error.required">Please provide your email address</span>
<span class="error" data-ng-show="fnolForm.email.$error.email">Please enter a valid email address</span>
</label>
</fieldset>
在我的控制器中我正在做:
$scope.submit = function() {
if ($scope.fnolForm.$valid) {
// client side validation has passed, do something ....
} else {
// client side validation has failed
$scope.fnolForm.submitError = true;
}
};
虽然我的验证工作正常,但它在许多方面感觉不对:
我确定有最后一点的解决方案(验证规则由服务器端验证备份);但前两个让我担心
这是否真的是以角度进行客户端验证的正确方法?我们真的必须在模板中编写规则吗?如果我们这样做,我们究竟应该如何对验证规则进行单元测试?
对此有任何意见(没有双关语:))
干杯
森
阅读完评论并重新阅读我已粘贴的代码后,我认为我原本很难在视图中接受fnolForm.submitError && fnolForm.name.$invalid
类型逻辑。
但是,经过反思,我现在可以看到,这实际上不是确定表单验证的业务逻辑,它纯粹是表示糖。 IE浏览器。我们可以删除所有这些,表单仍然具有相同的验证规则:
<form role="form" name="fnolForm" novalidate autocomplete="off" data-ng-submit="submit()">
<fieldset class="form-group">
<legend>Information about you</legend>
<label>
Your name
<input class="form-control" type="text" data-ng-model="fnol.insuredParty.name" name="name" required/>
</label>
<label>
Contact number
<input class="form-control" type="tel" data-ng-model="fnol.insuredParty.phoneNumber" name="phoneNumber" required/>
</label>
<label>
Email address
<input class="form-control" type="email" data-ng-model="fnol.insuredParty.email" name="email" required/>
</label>
</fieldset>
在上述情况下,我的表单验证规则由标准html5属性(required
,type="email"
等)确定,其中angularjs提供了跨浏览器逻辑,因此我不# 39; t需要进行单元测试。我知道angularjs为混合/最大长度等(指令)提供了自定义的附加字段验证器,所以我也没有测试它们。
但我仍然无法帮助我认为我需要将我的表单作为一个单元进行测试,特别是它是否有效。
例如,域业务规则可能会说只有在填写完姓名,电话号码和电子邮件字段,并且电子邮件地址字段包含有效(格式化)的电子邮件地址时才能提交表单。
在这种情况下,我想编写一系列单元测试,我用各种数据组合填充表单对象属性,并断言表单的有效性。
也许问题是我来自java背景,上面的模式是:单元测试肯定是我以前所使用的。
答案 0 :(得分:1)
好的,在考虑了所提供的各种选项,评论和答案后(感谢大家),我已经找到了一个基于编写验证服务的解决方案,并将其注入控制器。
这对我来说似乎是最重要的方框,因为这意味着我对什么构成有效形式及其可测试形式有明确的定义。但另外它还使用angularjs指令等来装饰表单(根据我原来的标记) - 演示糖:)
所以,这是我的最终解决方案。这是我以前的视图模板:
<form role="form" name="fnolForm" novalidate autocomplete="off" data-ng-submit="submit()">
<fieldset class="form-group">
<legend>Information about you</legend>
<label data-ng-class="{'has-error' : fnolForm.submitError && fnolForm.name.$invalid}">
Your name
<input class="form-control" type="text" data-ng-model="fnol.insuredParty.name" name="name" required/>
<span class="error" data-ng-show="fnolForm.submitError && fnolForm.name.$error.required">Please provide your name</span>
</label>
<label data-ng-class="{'has-error' : fnolForm.submitError && fnolForm.phoneNumber.$invalid}">
Contact number
<input class="form-control" type="tel" data-ng-model="fnol.insuredParty.phoneNumber" name="phoneNumber" required/>
<span class="error" data-ng-show="fnolForm.submitError && fnolForm.phoneNumber.$error.required">Please provide your contact number</span>
</label>
<label data-ng-class="{'has-error' : fnolForm.email.$error.email || (fnolForm.submitError && fnolForm.email.$invalid)}">
Email address
<input class="form-control" type="email" data-ng-model="fnol.insuredParty.email" name="email" required/>
<span class="error" data-ng-show="fnolForm.submitError && fnolForm.email.$error.required">Please provide your email address</span>
<span class="error" data-ng-show="fnolForm.email.$error.email">Please enter a valid email address</span>
</label>
</fieldset>
我写了一项服务如下:
fnolService.factory("FnolFormValidator",
[
function() {
return function(fnol) {
return !!(fnol &&
fnol.insuredParty &&
fnol.insuredParty.name &&
fnol.insuredParty.phoneNumber &&
fnol.insuredParty.email &&
fnol.insuredParty.email.match(/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i) !== null);
}
}
]
);
我将它注入我的控制器并按如下方式使用:
fnolControllers.controller("ReportAccidentController",
[
"$scope",
"$modal",
"fnol",
"FnolFormValidator",
function($scope, $modal, fnol, FnolFormValidator) {
$scope.submit = function() {
if ($scope.fnolForm.$valid && FnolFormValidator($scope.fnol)) {
// client side validation has passed, do something ....
} else {
// client side validation has failed
$scope.fnolForm.submitError = true;
}
};
我非常喜欢这个解决方案,因为它允许我单元测试表单的验证规则(即FnolFormValidator服务)以及控制器逻辑。如果我想测试演示糖,那么我可以用Selenium或类似的东西写一些东西 对我来说,这感觉就像是关注点的正确分离。
答案 1 :(得分:0)
我想这就是你如何看待它。我理解关于业务逻辑的关注点。我想我看到我不会那样看。它只是向用户显示反馈。发生这种情况的逻辑实际上位于表单对象中。所以你只是调用表达式来在其他地方进行评估。
我个人喜欢在视图中保留它。它感觉它属于那里(虽然它确实开始使简单的形式感觉有点膨胀)。但是如果你相反,你可以在控制器中实现它。 $scope.fnolForm.submitError
等。但是,如果您想要实时反馈,则需要在ng-change()
上加input
来模仿它。然后设置一个标记,让您的ng-show/hide
进行评估。
答案 2 :(得分:0)
为了回答你的第一点,它确实与表单验证有点混淆。 但是,设置这些错误值的所有业务逻辑都在视图后面处理,视图只是查看它们或它们的组合来决定如何显示错误。
但是视图中有很多代码,所以我的建议是使用指令作为输入。请参阅http://docs.angularjs.org/guide/directive。
指令可以将您的输入包装到1&#39;元素中。你可以传递重要的部分。该指令处理错误的所有代码和逻辑,因此您不需要在视图中直接执行此操作。您只需要一个表单元素作为指令的父元素,然后您可以将控制器作为指令链接函数的第4个参数拉出来。关于指令的文档很广泛,将涵盖所有这些。
希望有所帮助!
答案 3 :(得分:0)
这也是我在做客户端验证的方式。让我试着回答你的问题。
业务逻辑(验证规则与视图问题混淆)
验证规则(显然)不可测试
正如您所注意到的,即使您有这些不错的角度式客户端验证,也应始终使用服务器端验证。