核心要点
ng-required
和ng-pattern
)快速建立客户端输入验证。FormController
管理表单状态和验证,为用户提供即时反馈,提升用户体验。ng-submit
指令处理AngularJS中的表单提交,阻止默认提交行为,并在将数据发送到服务器之前启用自定义验证逻辑。许多开发者在对用户提交的数据执行复杂的业务约束时面临独特的挑战。最近,我在GiftCards.com编写应用程序时,我的团队也面临着这样的挑战。我们需要找到一种方法,允许我们的客户在一个视图中编辑多个产品,每个产品都有自己独特的验证规则。
这被证明具有挑战性,因为它要求我们在HTML源代码中使用多个<form></form>
标签,并为每个表单实例维护一个验证模型。在找到解决方案之前,我们尝试了许多方法,例如使用ngRepeat
显示子表单。最终,我们为每种产品类型创建一个指令(每个指令在其视图中都有一个<form></form>
),并让指令绑定到其父控制器。这使我们能够利用Angular的父子表单继承来确保只有在所有子表单都有效的情况下,父表单才有效。在本教程中,我们将构建一个简单的产品评论屏幕(突出显示我们当前应用程序的关键组件)。我们将有两个产品,每个产品都有自己的指令,并且每个产品都有独特的验证规则。将有一个简单的结账按钮,它将确保两个表单都有效。
如果您急于看到它的实际效果,您可以直接跳转到我们的演示,或从我们的GitHub存储库下载代码。
关于指令
指令是一段HTML代码,它通过AngularJS的HTML编译器($compile
)运行,并附加到DOM。编译器负责遍历DOM,查找它可以使用其他已注册指令转换为对象的组件。指令在一个隔离的作用域内工作,并维护自己的视图。它们是强大的工具,可以促进可重用的组件,这些组件可以在整个应用程序中共享。要快速复习,请查看这篇文章或AngularJS文档。
指令以两种方式解决了我们的根本问题:首先,每个实例都有一个隔离的作用域;其次,指令使用编译器传递,编译器使用Angular的ngForm
指令识别视图HTML中的表单元素。这个内置指令允许多个嵌套的表单元素,接受一个可选的name
属性来实例化FormController
,并将返回表单对象。
关于FormController
当编译器识别DOM中的任何表单对象时,它将使用ngForm
指令实例化一个FormController
对象。此控制器将扫描所有输入、选择和文本区域元素,并创建相应的控件。控件需要一个模型属性来设置双向数据绑定,并允许通过各种预构建的验证方法提供即时用户反馈。为消费者提供即时反馈,使他们能够在发出HTTP请求之前知道哪些信息有效。
预构建的验证方法
Angular打包了14种标准验证方法。这些包括min
、max
、required
等验证器。它们被构建为理解和操作几乎所有HTML5输入类型,并且与跨浏览器兼容。
<code class="language-html"><input type="text" ng-model="size" ng-required="true" novalidate> <span ng-show="myForm.size.$error.required"> Size: The value is required! </span></code>
上面的示例显示了在Angular中使用ngRequired
指令验证器的用法。此验证确保在字段被认为有效之前已填写该字段。它不验证任何数据,只是用户是否输入了某些内容。具有novalidate
属性表示浏览器不应在提交时进行验证。
专业提示:不要在任何Angular表单上设置
action
属性。这将阻止Angular尝试确保表单不会以往返方式提交。
自定义验证方法
Angular提供了一个广泛的API来帮助创建自定义验证规则。使用此API,您可以为标准验证中未涵盖的复杂输入创建和扩展您自己的验证规则。我的团队依靠一些自定义验证方法来运行我们的服务器使用的复杂正则表达式模式。如果没有运行复杂正则表达式匹配器的能力,我们可能会向后端服务器发送不正确的数据。这将向用户显示错误,从而导致不良的用户体验。自定义验证器使用指令语法,需要注入ngModel
。更多信息可以通过查阅AngularJS的文档来找到。
创建控制器
现在,我们可以开始我们的应用程序了。您可以在此处找到控制器代码的概述。
控制器将是事情的核心。它只有少数几个职责——它的视图将有一个名为parentForm
的表单元素,它只有一个属性,它的方法将包括registerFormScope
、validateChildForm
和checkout
。
控制器属性
我们需要控制器中的一个属性:
<code class="language-html"><input type="text" ng-model="size" ng-required="true" novalidate> <span ng-show="myForm.size.$error.required"> Size: The value is required! </span></code>
此属性用于维护表单整体有效性的布尔状态。我们使用此属性在单击“结账”按钮后禁用其状态。
方法:registerFormScope
<code class="language-javascript">$scope.formsValid = false;</code>
调用registerFormScope
时,将向其传递一个FormController
以及在指令实例化中创建的唯一指令ID。然后,此方法将表单作用域附加到父FormController
。
方法:validateChildForm
此方法将用于与执行验证的后端服务器协调。当用户正在编辑内容并且需要进行额外验证时,将调用它。从概念上讲,我们不允许指令执行任何外部通信。
请注意,出于本教程的目的,我省略了后端组件。相反,我根据用户输入的金额是否在特定范围内(产品A为10-50,产品B为25-500),拒绝或解析promise。
<code class="language-javascript">$scope.registerFormScope = function (form, id) { $scope.parentForm['childForm'+id] = form; };</code>
使用$q
服务允许指令遵守具有成功和失败状态的接口。“编辑”和“保存”之间的应用程序接口的性质取决于模型数据的编辑。应该注意的是,模型数据在用户开始键入时就会立即更新。
方法:Checkout
单击“结账”表示用户已完成编辑并希望结账。此可操作项目需要验证在指令中加载的所有表单都通过验证,然后才能将模型数据发送到服务器。本文的范围不包括用于将数据发送到服务器的方法。我鼓励您探索使用$http
服务进行所有客户端到服务器的通信。
<code class="language-javascript">$scope.validateChildForm = function (form, data, product) { // 重置表单,使其不再有效 $scope.formsValid = false; var deferred = $q.defer(); // 验证表单和数据的逻辑 // 必须在promise上返回resolve()或reject()。 $timeout(function () { if (angular.isUndefined(data.amount)) { return deferred.reject(['amount']); } if ((data.amount < product.minAmount) || (data.amount > product.maxAmount)) { return deferred.reject(['amount']); } deferred.resolve(); }); return deferred.promise; }</code>
此方法使用Angular的能力,子表单可以使父表单无效。父表单名为parentForm
,以清楚地说明其与子表单的关系。当子表单使用其$setValidity
方法时,它将自动上升到父表单以在那里设置有效性。parentForm
中的所有表单都必须有效,其内部$valid
属性才能为true。
创建我们的指令
我们的指令必须遵循一个通用的接口,该接口允许完全互操作性和可扩展性。我们的指令的名称取决于它们包含的产品。
您可以在此处(产品A)和此处(产品B)找到指令代码的概述。
隔离指令作用域
每个实例化的指令都将获得一个隔离的作用域,该作用域被本地化到指令,并且不知道外部属性。但是,AngularJS允许创建使用父作用域方法和属性的指令。当将外部属性传递到本地作用域时,您可以指示要设置双向数据绑定。
我们的应用程序将需要一些外部双向数据绑定方法和属性:
<code class="language-javascript">$scope.checkout = function () { if($scope.parentForm.$valid) { // 连接服务器以发布数据 } $scope.formsValid = $scope.parentForm.$valid; };</code>
指令本地作用域中的第一个属性是将本地作用域.form注册到控制器的方法。指令需要一个管道将本地FormController
对象传递给主控制器。
这是将在指令视图中使用的集中式模型数据。此信息将进行双向数据绑定,以确保在FormController
中发生的更新将传播到主控制器。
此方法与控制器中定义的方法相同。当用户更新指令视图中的信息时,将调用此方法。
此对象包含有关正在购买的产品的信息。我们的演示使用一个相对较小的对象,只有少数几个属性。我的团队的现实世界应用程序有很多信息用于在应用程序中做出决策。它被传递到validateChildForm
中,以提供正在验证的内容的上下文。
指令链接
我们的指令将使用postLink
函数,并向其传递一个作用域对象。除此之外,postLink
函数还接受其他几个参数。它们如下:
scope
– 用于访问为每个指令实例创建的隔离作用域。iElement
– 用于访问元素项。只有在其分配到的元素内,从postLink
函数中更新和修改该元素才是安全的。iAttrs
– 用于访问在实例化指令的同一标签上的属性。controller
– 如果存在外部控制器依赖项,则可以在链接函数中使用。这些必须与指令对象的require
属性相对应。transcludeFn
– 该函数与指令对象的$transclude
参数中列出的函数相同。link
负责附加所有DOM侦听器并使用视图元素更新DOM。
<code class="language-html"><input type="text" ng-model="size" ng-required="true" novalidate> <span ng-show="myForm.size.$error.required"> Size: The value is required! </span></code>
<code class="language-javascript">$scope.formsValid = false;</code>
将方法registerFormScope
包装在$timeout
中会将其执行延迟到执行堆栈的末尾。这为编译器提供了足够的时间来完成控制器和指令之间的所有必要链接。scope.form.fields
是一个数组,是FormController
中找到的属性的名称,这对于设置服务器端验证错误非常重要。registerFormScope
的目的是将FormController
发送到父控制器,允许新创建的表单设置为parentForm
的子表单。
<code class="language-javascript">$scope.registerFormScope = function (form, id) { $scope.parentForm['childForm'+id] = form; };</code>
当表单发生更改并且用户准备好对其进行验证时,将调用指令中的saveForm
方法。此方法将依次调用控制器的validateChildForm
方法,并传入FormController
、scope.giftData
和scope.product
。控制器返回一个promise,该promise将根据其他验证规则被解析或拒绝。
当promise被拒绝时,控制器将返回无效的字段。这用于使表单(和parentForm
)无效,并设置其他字段级别错误。在我们的演示中,我们在amount
字段上使用简单的后验证,并且我们不返回失败的原因。来自validateChildForm
的拒绝可以像您的应用程序需要的那么复杂或简单。
当promise成功返回时,指令需要设置表单中字段的有效性。代码还必须清除任何先前识别的服务器错误。这确保指令不会错误地向用户提供错误。使用$setValidity
设置所有字段会链接到控制器中的parentForm
,以设置其有效性,前提是所有子表单都有效。
设置我们的视图
视图不是很复杂,为了我们的演示,我们将产品简化为以下字段:名称和金额。在下一步中,我们将探讨完成此应用程序所需的视图。
您可以在此处(产品A)和此处(产品B)找到视图代码的概述。
路由视图
<code class="language-html"><input type="text" ng-model="size" ng-required="true" novalidate> <span ng-show="myForm.size.$error.required"> Size: The value is required! </span></code>
此视图很重要,因为它设置了父表单,该表单将用于包装从产品指令加载的所有子表单。在ng-repeat
中使用ng-if
可确保不会使用未使用的FormController
错误地填充DOM。
指令视图
<code class="language-javascript">$scope.formsValid = false;</code>
注意:上面关于演示布局的视图在某些地方已被截断,与本文无关。
上面的amountInput
设置了一个验证模式,该模式将由Angular的ngPattern
验证器强制执行。上面的字段将使用Angular构建的ngDisabled
指令,该指令评估表达式,如果为true,则字段将被禁用。
在视图底部,我们显示所有错误,以便在用户单击“保存”按钮时向用户提供反馈。这将设置子表单上的$submitted
属性。
总结
将所有部分放在一起,我们最终得到以下内容:(此处应插入CodePen链接或代码片段)
您也可以在GitHub上找到所有代码。
结论
我的团队在构建我们的最新应用程序时学到了很多东西。了解父子表单关系使我们能够简化我们的评论屏幕。使用指令使我们能够开发一个可以在任何上下文中使用的表单,并促进良好的可重用代码。指令还使我们能够进行单元测试代码,以确保我们的表单按预期工作。我们的应用程序正在生产中,并且已经促成了超过100,000个订单。
我希望您喜欢阅读这篇文章。如果您有任何问题或意见,我很乐意在下面的评论中听到它们。
AngularJS中基于表单的指令的常见问题解答(FAQ)
AngularJS中基于表单的指令在管理和验证表单中的用户输入方面发挥着至关重要的作用。它们提供了一种创建自定义HTML标签的方法,这些标签充当新的自定义窗口小部件。它们还可以以向我们的应用程序添加功能的方式来操作DOM。当您希望在整个应用程序中封装和重用通用功能时,这些指令尤其有用。
在AngularJS中创建自定义基于表单的指令涉及使用.directive
函数定义一个新指令。您需要为您的指令提供一个名称和一个工厂函数,该函数将生成指令的选项对象。此对象可以定义多个属性,包括restrict
、template
、scope
、link
等等。restrict
选项用于指定如何在HTML中调用指令。
AngularJS提供了一些用于表单验证的内置指令,包括ng-required
、ng-pattern
、ng-minlength
、ng-maxlength
等等。这些指令向您的表单输入添加了验证功能,确保用户输入在表单提交之前满足某些条件。您还可以为更复杂的验证要求创建自定义验证指令。
AngularJS中的FormController
提供用于跟踪表单及其控件的状态、检查有效性和重置表单的方法。它在表单指令内自动可用,可以注入到控制器、其他指令或服务中。
AngularJS中的ng-submit
指令允许您在提交表单时指定自定义行为。您可以使用ng-submit
在事件发生时执行表达式,而不是编写JavaScript代码来处理表单的提交事件。这在表单无效时阻止默认表单提交行为时尤其有用。
AngularJS中form
和ng-form
的主要区别在于ng-form
可以嵌套在其他表单中。这允许您将相关的输入组合在一起并将其作为一个子表单进行验证。另一方面,标准的form
指令不支持嵌套。
您可以使用ngModelController
提供的$setValidity
方法来设置AngularJS中表单字段的有效性。此方法接受两个参数:验证键和布尔值。如果布尔值为false,则该键将添加到字段的$error
对象中。
AngularJS中的ng-model
指令将输入、选择、文本区域或自定义表单控件绑定到作用域上的属性。它在模型和视图之间提供双向数据绑定。这意味着对输入字段的任何更改都将自动更新模型,反之亦然。
AngularJS中的ng-change
指令允许您在用户更改输入时指定自定义行为。当您希望在用户完成键入或进行选择后立即执行某些操作,而不是等待表单提交时,此指令很有用。
在AngularJS中创建自定义验证指令涉及定义一个需要ngModel
的新指令。在指令的link
函数中,您可以使用ngModelController
的$validators
或$asyncValidators
管道来添加自定义验证逻辑。
以上是如何在Angularjs中创建基于表单的指令的详细内容。更多信息请关注PHP中文网其他相关文章!