Home >Web Front-end >JS Tutorial >How to Create Form-Based Directives in AngularJS
Core points
ng-required
and ng-pattern
) to quickly establish client input verification. FormController
in AngularJS to manage form status and verification to provide users with instant feedback and improve user experience. ng-submit
directive to handle form submissions in AngularJS, blocking the default submission behavior, and enabling custom verification logic before sending data to the server. Many developers face unique challenges when executing complex business constraints on user-submitted data. Recently, my team faced this challenge when I was writing an application at GiftCards.com. We need to find a way to allow our customers to edit multiple products in one view, each with its own unique verification rules.
This proves challenging because it requires us to use multiple <form></form>
tags in the HTML source code and maintain a validation model for each form instance. Before we find the solution, we tried many methods, such as using ngRepeat
to display subforms. Finally, we create a directive for each product type (each directive has a <form></form>
in its view) and have the directive bind to its parent controller. This allows us to leverage Angular's parent-child form inheritance to ensure that the parent form is valid only if all child forms are valid. In this tutorial, we will build a simple product review screen (highlighting the key components of our current application). We will have two products, each with its own instructions and each product has unique verification rules. There will be a simple checkout button, which will ensure both forms are valid.
If you are anxious to see how it actually works, you can jump directly to our demo or download the code from our GitHub repository.
About the command
The directive is a piece of HTML code that runs through AngularJS's HTML compiler ($compile
) and is attached to the DOM. The compiler is responsible for traversing the DOM and finding components that it can convert into objects using other registered directives. Directives work within an isolated scope and maintain their own views. They are powerful tools that facilitate reusable components that can be shared throughout the application. For a quick review, check out this article or AngularJS documentation.
directive solves our fundamental problem in two ways: first, each instance has an isolated scope; second, the directive is passed using the compiler, which uses the Angular ngForm
directive to recognize the Form element. This built-in directive allows multiple nested form elements to accept an optional name
attribute to instantiate FormController
and will return the form object.
About FormController
When the compiler recognizes any form object in the DOM, it will instantiate a ngForm
object using the FormController
directive. This controller will scan all input, selection, and text area elements and create corresponding controls. The control requires a model property to set up two-way data binding and allows instant user feedback through various pre-built verification methods. Provide instant feedback to consumers, allowing them to know what information is valid before making HTTP requests.
Prebuilt verification method
Angular packages 14 standard verification methods. These include min
, max
, required
and other validators. They are built to understand and manipulate almost all HTML5 input types and are cross-browser-compatible.
<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>
The above example shows how to use the ngRequired
directive validator in Angular. This verification ensures that the field is filled in before it is considered valid. It does not verify any data, it just does the user enter something. Having the novalidate
attribute means that the browser should not verify when submitting.
Pro tip: Do not set
action
attributes on any Angular form. This will prevent Angular from trying to make sure the form is not submitted in a round trip manner.
Custom verification method
Angular provides an extensive API to help create custom validation rules. With this API, you can create and extend your own validation rules for complex inputs not covered in standard validation. My team relies on some custom validation methods to run the complex regular expression patterns our server uses. Without the ability to run a complex regular expression matcher, we may send incorrect data to the backend server. This will show errors to the user, resulting in a bad user experience. Custom validators use directive syntax and need to be injected ngModel
. More information can be found by consulting the AngularJS documentation.
Create the controller
Now, we can start our application. You can find an overview of the controller code here.
The controller will be at the heart of things. It has only a few responsibilities - its view will have a form element named parentForm
, it has only one attribute, and its methods will include registerFormScope
, validateChildForm
and checkout
.
Controller properties
We need a property in the controller:
<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>
This property is used to maintain the Boolean state of the overall validity of the form. We use this property to disable its status after clicking the Checkout button.
Method: registerFormScope
<code class="language-javascript">$scope.formsValid = false;</code>When
is called, a registerFormScope
and a unique directive ID created in the instruction instantiation are passed to it. This method then appends the form scope to the parent FormController
. FormController
Method: validateChildForm
This method will be used to coordinate with the backend server that performs the verification. It is called when the user is editing the content and needs additional verification. Conceptually, we do not allow instructions to perform any external communication.Please note that for the purposes of this tutorial, I omitted the backend components. Instead, I reject or parse promises based on whether the amount entered by the user is within a specific range (product A is 10-50 and product B is 25-500).
<code class="language-javascript">$scope.registerFormScope = function (form, id) { $scope.parentForm['childForm'+id] = form; };</code>Using the
service allows instructions to comply with interfaces with success and failure status. The nature of the application interface between "Edit" and "Save" depends on the editing of the model data. It should be noted that the model data is updated immediately when the user starts typing. $q
Method: Checkout
Click "Checkout" to indicate that the user has completed editing and wants to checkout. This actionable item requires verification that all forms loaded in the directive are validated before the model data can be sent to the server. The scope of this article does not include methods for sending data to a server. I encourage you to explore using the service for all client-to-server communication. $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>This method uses Angular's ability, and child forms can invalidate the parent form. The parent form is named
to clearly illustrate its relationship with the child form. When the child form uses its parentForm
method, it will automatically rise to the parent form to set validity there. All forms in $setValidity
must be valid, and their internal parentForm
attributes must be true. $valid
Create our directive
Our instructions must follow a common interface that allows for full interoperability and scalability. The name of our directives depends on the product they contain.You can find an overview of the instruction code here (product A) and here (product B).
Scope of the isolation instruction
Each instantiated directive will get an isolated scope that is localized to the directive and has no external properties known. However, AngularJS allows the creation of directives that use parent scope methods and properties. When passing external properties to the local scope, you can indicate that you want to set up a two-way data binding.Our application will require some external two-way data binding methods and properties:
<code class="language-javascript">$scope.checkout = function () { if($scope.parentForm.$valid) { // 连接服务器以发布数据 } $scope.formsValid = $scope.parentForm.$valid; };</code>Method: registerFormScope
object to the main controller. FormController
This is the centralized model data that will be used in the instruction view. This information will be bidirectional data binding to ensure that updates occurring in FormController
will propagate to the main controller.
This method is the same as the one defined in the controller. This method is called when the user updates information in the instruction view.
This object contains information about the product being purchased. Our demonstration uses a relatively small object with only a few properties. My team's real-world application has a lot of information to make decisions in the application. It is passed into validateChildForm
to provide the context of the content being verified.
Instruction link
Our directive will use the postLink
function and pass it a scope object. In addition, the postLink
function also accepts several other parameters. They are as follows:
scope
– Used to access the isolation scope created for each directive instance. iElement
– Used to access element items. It is safe to update and modify the element from the postLink
function within the element it is allocated to. iAttrs
– Used to access properties on the same tag of the instantiation directive. controller
– If an external controller dependency exists, it can be used in a link function. These must correspond to the require
attribute of the directive object. transcludeFn
– This function is the same as the function listed in the $transclude
parameter of the instruction object. link
Responsible for attaching all DOM listeners and updating the DOM with view elements.
<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>
Wrapping method registerFormScope
in $timeout
will delay its execution until the end of the execution stack. This provides the compiler with enough time to complete all the necessary links between the controller and the instructions. scope.form.fields
is an array, the name of the property found in FormController
, which is very important for setting up server-side verification errors. The purpose of registerFormScope
is to send FormController
to the parent controller, allowing the newly created form to be set to a child form of parentForm
.
<code class="language-javascript">$scope.registerFormScope = function (form, id) { $scope.parentForm['childForm'+id] = form; };</code>
When the form changes and the user is ready to verify it, the saveForm
method in the directive is called. This method will invoke the controller's validateChildForm
method in turn and pass in FormController
, scope.giftData
and scope.product
. The controller returns a promise that will be parsed or rejected according to other validation rules.
When promise is rejected, the controller will return an invalid field. This is used to invalidate the form (and parentForm
) and set other field level errors. In our demo, we use simple post-verification on the amount
field and we do not return the cause of failure. Rejections from validateChildForm
can be as complex or simple as your application needs to.
When promise returns successfully, the command needs to set the validity of the fields in the form. The code must also clear any previously identified server errors. This ensures that the directive does not provide errors to the user incorrectly. Setting all fields with $setValidity
will be linked to parentForm
in the controller to set their validity, provided that all subforms are valid.
Set our view
The view is not very complicated, and for our demonstration we simplified the product to the following fields: name and amount. In the next step, we will explore the views you need to complete this application.
You can find an overview of the view code here (Product A) and here (Product B).
Route View
<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>
This view is important because it sets up the parent form, which will be used to wrap all child forms loaded from the product directive. Using ng-repeat
in ng-if
ensures that the DOM is not filled incorrectly with an unused FormController
.
Instruction View
<code class="language-javascript">$scope.formsValid = false;</code>
Note: The above view on the demo layout has been truncated in some places and has nothing to do with this article.
The above amountInput
sets a verification mode which will be enforced by Angular's ngPattern
validator. The above field will use the ngDisabled
directive built by Angular, which evaluates the expression, and if true, the field will be disabled.
At the bottom of the view, we display all errors to provide feedback to the user when the user clicks the "Save" button. This will set the $submitted
property on the subform.
Summary
Put all the pieces together and we end up with the following: (CodePen link or code snippet should be inserted here)
You can also find all the code on GitHub.
Conclusion
My team learned a lot when building our latest apps. Understanding the father-son form relationship allows us to simplify our comment screen. Using directives allows us to develop a form that can be used in any context and promotes good reusable code. The directive also enables us to unit test the code to ensure our form works as expected. Our application is in production and has contributed to over 100,000 orders.
I hope you enjoyed reading this article. If you have any questions or comments, I'd love to hear them in the comments below.
FAQ for Form-Based Directives in AngularJS
Form-based directives in AngularJS play a crucial role in managing and validating user input in forms. They provide a way to create custom HTML tags that act as new custom widgets. They can also manipulate DOMs in a way that adds functionality to our applications. These instructions are especially useful when you want to encapsulate and reuse common features throughout your application.
Creating a custom form-based directive in AngularJS involves defining a new directive using the .directive
function. You need to provide your directive with a name and a factory function that will generate the option object of the directive. This object can define multiple properties, including restrict
, template
, scope
, link
, and so on. The restrict
option is used to specify how directives are called in HTML.
AngularJS provides some built-in instructions for form validation, including ng-required
, ng-pattern
, ng-minlength
, ng-maxlength
, and more. These instructions add validation to your form input, ensuring that user input meets certain criteria before the form is submitted. You can also create custom verification directives for more complex verification requirements.
FormController
in AngularJS provides methods for tracking the status of forms and their controls, checking validity, and resetting forms. It is automatically available within the form directive and can be injected into the controller, other directives, or services.
directive in AngularJS allows you to specify custom behavior when submitting a form. Instead of writing JavaScript code to handle the form's submission event using ng-submit
. This is especially useful when preventing the default form submission behavior when the form is invalid. ng-submit
in form
in ng-form
is that ng-form
can be nested in other forms. This allows you to group relevant inputs together and validate them as a subform. On the other hand, the standard form
directives do not support nesting.
You can set the validity of form fields in AngularJS using the ngModelController
method provided by $setValidity
. This method accepts two parameters: the validation key and the boolean value. If the boolean value is false, the key is added to the field's $error
object.
The ng-model
directive in AngularJS binds input, selection, text area, or custom form control to properties on scope. It provides two-way data binding between the model and the view. This means that any changes to the input field will automatically update the model and vice versa.
directive in AngularJS allows you to specify custom behavior when the user changes input. This directive is useful when you want to perform something immediately after the user has finished typing or making a selection instead of waiting for the form to be submitted. ng-change
. In the ngModel
function of the directive, you can use the link
or ngModelController
pipeline to add custom validation logic. $validators
The above is the detailed content of How to Create Form-Based Directives in AngularJS. For more information, please follow other related articles on the PHP Chinese website!