I've followed this tutorial about AngularJS Multi-Step Form Using UI Router. The form works and I can save my data but now I'm having questions about how to validate each step in the form.
I have the following form with input fields:
Step 1
- License Plate
Step 2
- Name
- Street
- Zipcode
- City
- Telephone
Step 3
- Choose a date & time from a calendar
It looks somewhat like this:
I have a general base view like this:
<body ng-app="formApp">
<div id="top"></div>
<div class="container">
<!-- views will be injected here -->
<div ui-view></div>
</div>
</body>
In my app.js I have the following (not complete, left the non important things out):
// app.js
// create our angular app and inject ngAnimate and ui-router
// =============================================================================
angular.module('formApp', ['ngAnimate', 'ui.router', 'ui.calendar'])
// configuring our routes
// =============================================================================
.config(function($stateProvider, $urlRouterProvider, $interpolateProvider) {
$interpolateProvider.startSymbol('<%');
$interpolateProvider.endSymbol('%>');
$stateProvider
// route to show our basic form (/form)
.state('form', {
url: '/form',
templateUrl: 'views/form.html',
controller: 'formController'
})
// nested states
// each of these sections will have their own view
// url will be /form/interests
.state('form.license', {
url: '/license',
templateUrl: 'views/form-license.html'
})
// url will be nested (/form/profile)
.state('form.profile', {
url: '/profile',
templateUrl: 'views/form-profile.html'
})
// url will be /form/payment
.state('form.appointment', {
url: '/appointment',
templateUrl: 'views/form-appointment.html'
})
// url will be /form/success
.state('form.success', {
url: '/success',
templateUrl: 'views/form-success.html'
});
// catch all route
// send users to the form page
$urlRouterProvider.otherwise('/form/license');
})
// our controller for the form
// =============================================================================
.controller('formController', function($scope, $http, $compile, $location, uiCalendarConfig) {
$scope.formData = {};
$scope.formData.profile = {};
$scope.next = function(step){
if(step == 1)
{
}
else if(step == 2)
{
}
};
// function to process the form
$scope.processForm = function(isValid) {
};
});
My general form.html:
<!-- form.html -->
<div class="row">
<div class="col-sm-6 col-sm-offset-3">
<div id="form-container">
<form id="appointment-form" name="appointmentform" ng-submit="processForm(appointmentform.$valid)">
<!-- our nested state views will be injected here -->
<div id="form-views" ui-view></div>
</form>
</div>
</div>
</div>
The first step in my form is in form-license.html:
<!-- form-license.html -->
<label>Nummerplaat ingeven</label>
<div class="form-group">
<div class="col-xs-8 col-xs-offset-2">
<input required type="text" class="form-control" name="license" ng-model="formData.license">
</div>
</div>
<div class="form-group row">
<div class="col-xs-4 col-xs-offset-4">
<a ng-click="next(1)" ui-sref="form.profile" class="btn btn-next btn-block">
Volgende
</a>
</div>
</div>
But now I'm wondering how I can validate this when they click on next button ... . It's not working with the normal required attribute.
Can somebody help me with this?
UPDATE:
Now I have in my first step the following:
<div class="col-xs-4 col-xs-offset-4">
<a ng-click="next(1, processForm)" ui-sref="form.profile" ng-disabled="!licenseValidated" class="btn btn-next btn-block">
Volgende
</a>
</div>
In my controller:
var validateLicense = function (newVal) {
var validated = false;
// Run your custom validation checks
if(newVal)
{
validated = true;
}
return validated;
};
$scope.$watch('formData.license', function (newVal) {
$scope.licenseValidated = validateLicense(newVal);
});
Ok, that works. But in my second step I have multiple fields like this:
<div class="profile">
<div class="form-group">
<label class="col-sm-3 control-label" for="name">Name</label>
<div class="col-sm-9">
<input type="text" class="form-control" name="name" ng-model="formData.profile.name">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label" for="street">Street</label>
<div class="col-sm-9">
<input type="text" class="form-control" name="street" ng-model="formData.profile.street">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label" for="zipcode">Zipcode</label>
<div class="col-sm-9">
<input type="text" class="form-control" name="zipcode" ng-model="formData.profile.zipcode">
</div>
</div>
<div class="form-group row">
<div class="col-xs-8 col-xs-offset-2">
<a ng-click="next(1)" ui-sref="form.license" class="btn btn-block btn-previous col-xs-3">
VORIGE
</a>
<a ng-click="next(2)" ui-sref="form.appointment" class="btn btn-block btn-next col-xs-3">
Volgende
</a>
</div>
</div>
</div>
Do I need to create for every one of them a $scope.watch? And do I need to add them to ng-disabled of my button?
AngularJS performs form validation on the client side. AngularJS monitors the state of the form and input fields (input, text-area, select), and notify the user about the current state. AngularJS also holds information about whether the input fields have been touched, modified, or not.
ng-touched The field has been touched. ng-pristine The field has not been modified yet. ng-dirty The field has been modified. ng-valid The field content is valid.
How do you clear form data after submit in AngularJS? 1) To Remove the values in Form Fields and to reset you can use $setPristine(); $scope. formName. $setPristine();
To allow styling of form as well as controls, ngModel adds these CSS classes: ng-valid : the model is valid. ng-invalid : the model is invalid. ng-valid-[key] : for each valid key added by $setValidity.
You could simply disable the next button if any of the validation steps doesn't pass.
Something like:
// Inside your controller.
// Don't overload the scope.
// Only assign what will be needed through your HTML or other AngularJS Scopes
var validateLicense = function (newVal) {
// If you are only checking for content to be entered
return (newVal !== '' && newVal !== undefined);
};
var validateInfo = function (newVal) {
if (newVal.length > 0) {
// Check to make sure that all of them have content
for (var i = 0, l = newVal.length; i < l; i++) {
if (newVal[i] === undefined || newVal[i] === '') {
return false;
}
}
// We didn't find invalid data, let's move on
return true;
}
return false;
};
var validateDate = function (newVal) {
var validated = false;
// Run your custom validation checks
return validated;
}
// Initialize the disabled "Next" buttons
$scope.licenseValidated = false;
$scope.infoValidated = false;
// Watch a single item in a form, if filled in we will let them proceed
$scope.$watch('formData.license', function (newVal) {
$scope.licenseValidated = validateLicense(newVal);
});
// Watch a multiple items in a form, if ALL are filled in we will let them proceed
// Note that the order in this array is the order the newVal will be,
// So further validation for formData.number would be on newVal[1]
$scope.$watchGroup(['formData.name', 'formData.number', 'formData.address'], function (newVal) {
$scope.infoValidated = validateInfo(newVal);
});
form-license.html add the ng-disabled
attribute on your next button:
<a ng-click="next(1, appointmentform)" ui-sref="form.profile" class="btn btn-next btn-block" ng-disabled="!licenseValidated">
Volgende
</a>
form-info.html repeat above steps
<a ng-click="next(1, appointmentform)" ui-sref="form.profile" class="btn btn-next btn-block" ng-disabled="!infoValidated">
Volgende
</a>
And so on...
See this Fiddle for Demo
You have a couple of options available to you depending on how you want to approach it.
To start, you should use ng-form
for each of the 3 form steps. This will allow you to validate each individually without having to worry about the other sections.
So as an example your first form step might turn into:
<ng-form name="LicenseForm">
<label>Nummerplaat ingeven</label>
<div class="form-group">
<div class="col-xs-8 col-xs-offset-2">
<input required type="text" class="form-control" name="license" ng-model="formData.license">
</div>
</div>
<div class="form-group row">
<div class="col-xs-4 col-xs-offset-4">
<a ng-click="next(1, LicenseForm)" ui-sref="form.profile" class="btn btn-next btn-block">
Volgende
</a>
</div>
</div>
</ng-form>
This gives you access to the form validation properties for just this step. At this point you can update your controller to use .$invalid
or .$valid
on the form object that is now being passed in the next
submit button, meaning you can now do something like:
$scope.next = function(step, form) {
if (form.$invalid) {
console.log('Form is invalid!');
return;
}
// move to next section
...
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With