It is a good practice to validate submitted data both on the frontend and the backend. It is also good, talking about validation, to distinguish the user experience, from the data integrity conservation from the data integrity conservation. Both are two different responsibilities, potentially for different teams.
We believe the frontend validation has replaced the form-validations that was previously managed by the backend. In a scalable environment where API is decoupled from web content, the validation experiences are now the responsibility of client interfaces that can be multiples (even implemented by third parties) such as websites, mobile websites, mobile apps, and so on.
In this recipe, we will focus on form validation and more specifically on AngularJS form validation.
user-account.html
):<form name="updateAccount" action="#" ng-class="formSubmitted ? 'submitted':''"> <fieldset> <div class="clearfix span"> <label for="id" translate> screen.preference.field.username</label> <div class="input"> <input type="text" name="id" placeholder="Username" ng-model="form.id" ng-minlength="4" ng-maxlength="15" readonly required/> <span class="text-error" ng-show="formSubmitted && updateAccount.id.$error.required" translate> error.webapp.user.account.username.required</span> </div> <label for="email" translate> screen.preference.field.email</label> <div class="input"> <input type="email" name="email" placeholder="Email" ng-model="form.email"/> <span class="text-error" ng-show="formSubmitted && updateAccount.email.$error" translate>error.webapp.user.account.email</span> </div> <label for="password" translate> screen.preference.field.password</label> <div class="input"> <input type="password" name="password" ng-minlength="5" placeholder="Please type again" ng-model="form.password" required/> <span class="text-error" ng-show="formSubmitted && updateAccount.password.$error.required" translate> error.webapp.user.account.password.type.again</span> <span class="text-error" ng-show="formSubmitted && updateAccount.password.$error.minlength" translate> error.webapp.user.account.password.too.short</span> </div> <label for="fullname" translate> screen.preference.field.full.name</label> <div class="input" > <input type="text" name="fullname" placeholder="Full name" ng-model="form.fullname"/> </div> <label for="currencySelector" translate> screen.preference.field.preferred.currency</label> <div class="input"> <select class="input-small" id="currencySelector" ng-model="form.currency" ng-init="form.currency='USD'" ng-selected="USD" ng-change="updateCredit()"> <option>USD</option><option>GBP</option> <option>EUR</option><option>INR</option> <option>SGD</option><option>CNY</option> </select> </div> <label for="currencySelector" translate> screen.preference.field.preferred.language</label> <div class="input"> <div class="btn-group"> <button onclick="return false;" class="btn" tabindex="-1"><span class="lang-sm lang-lbl" lang="{{form.language | lowercase}}"></button> <button class="btn dropdown-toggle" data-toggle="dropdown" tabindex="-1"> <span class="caret"></span> </button> <ul class="dropdown-menu"> <li><a href="#" ng-click="setLanguage('EN')"><span class="lang-sm lang-lbl-full" lang="en"></span></a></li> <li><a href="#" ng-click="setLanguage('FR')"> <span class="lang-sm lang-lbl-full" lang="fr"></span></a></li> </ul> </div> </div> </div> </fieldset> </form>
account_management.js
includes two referenced functions and four variables to control form validation and its style:$scope.update = function () { $scope.formSubmitted = true; if(!$scope.updateAccount.$valid) { return; } httpAuth.put('/api/users', JSON.stringify($scope.form)).success( function(data, status, headers, config) { httpAuth.setCredentials( $scope.form.id, $scope.form.password); $scope.updateSuccess = true; }).error(function(data,status,headers,config) { $scope.updateFail = true; $scope.updateSuccess = false; $scope.serverErrorMessage = errorHandler.renderOnForms(data); }); }; $scope.setLanguage = function(language) { $translate.use(language); $scope.form.language = language; } //Variables initialization $scope.formSubmitted = false; $scope.serverErrorMessage =""; $scope.updateSuccess = false; $scope.updateFail = false;
Two CSS classes have been created to render properly errors on fields:
.submitted input.ng-invalid{ border: 2px solid #b94a48; background-color: #EBD3D5;!important; } .submitted .input .text-error { font-weight:bold; padding-left:10px; }
AngularJS provides tools to set up a client-side form validation. As usual with AngularJS, these tools integrate well with modern HTML5 techniques and standards.
HTML5 forms provide a native validation that can be defined using tags and attributes on the different form elements (input, select…) to set up a basic field validation (max-length, required…)
AngularJS completes and extends fluently these standard definitions to make them interactive and responsive from the beginning and with no overhead.
Let's have a closer look at the available validation-options that can be placed on form controls.
The ng-minlength
directive can be used to assert that the number of entered characters matches a given threshold:
<input type="text" ng-minlength="3" />
Similarly, ng-maxlength
drastically limits the number of entered characters to a maximum:
<input type="text" ng-maxlength="15" />
The ng-pattern
directive is often used to make sure that the entered data matches a predefined shape:
<input type="text" ng-pattern="[a-zA-Z]" />
Those HTML5 input types are handled by AngularJS to be constrained within the format they represent:
<input type="number" name="quantity" ng-model="form.quantity" /> <input type="email" name="email" ng-model=" form.email" /> <input type="url" name="destination" ng-model=" form.url" />
AngularJS publishes properties on the containing $scope
to match the form state in the DOM. This makes the JavaScript form validation very easy to control errors and to render the state.
These properties are accessible from the following structure:
formName.inputFieldName.property
This state can be assessed using the following properties:
formName.inputFieldName.$pristine; formName.inputFieldName.$dirty;
This valid state of a form can be assessed in regards to the defined validation for a field or globally:
formName.inputFieldName.$valid; formName.inputFieldName.$invalid; formName.$valid; formName.$invalid;
After the validity assessment that we have defined previously, more information about what went wrong can be extracted from the $error
property:
myForm.username.$error.pattern myForm.username.$error.required myForm.username.$error.minlength
The $error
object contains all about the validations of a particular form and reflects whether those validations are satisfactory or not.
As often with AngularJS, transclusions are proceeded to bind the DOM state with the scope. Thus, the form state and the control state are reflected in real time with CSS classes. These CSS classes can obviously be defined/overridden, so that a global validation style can be defined:
input.ng-invalid { border: 1px solid red; } input.ng-valid { border: 1px solid green; }