Multistep form using angular ui router

I went through this article http://scotch.io/tutorials/javascript/angularjs-multi-step-form-using-ui-router#building-our-angular-app-app.js

and the plunkr link is http://plnkr.co/edit/M03tYgtfqNH09U4x5pHC?p=preview

My question is how to incorporate form validation within this process, let say for the first form where we have name and the email, how we can we validate it first and restrict moving to the next form if fields are empty etc. or email is invalid.

// create our angular app and inject ngAnimate and ui-router 
// =============================================================================
angular.module('formApp', ['ngAnimate', 'ui.router'])

// configuring our routes 
// =============================================================================
   .config(function($stateProvider, $urlRouterProvider) {

$stateProvider

    // route to show our basic form (/form)
    .state('form', {
        url: '/form',
        templateUrl: 'form.html',
        controller: 'formController'
    })

    // nested states 
    // each of these sections will have their own view
    // url will be nested (/form/profile)
    .state('form.profile', {
        url: '/profile',
        templateUrl: 'form-profile.html'
    })

    // url will be /form/interests
    .state('form.interests', {
        url: '/interests',
        templateUrl: 'form-interests.html'
    })

    // url will be /form/payment
    .state('form.payment', {
        url: '/payment',
        templateUrl: 'form-payment.html'
    });

// catch all route
// send users to the form page 
$urlRouterProvider.otherwise('/form/profile');
})

// our controller for the form
// =============================================================================
.controller('formController', function($scope) {

// we will store all of our form data in this object
$scope.formData = {};

// function to process the form
$scope.processForm = function() {
    alert('awesome!');  
};

});

Answers


So the simple answer is you can just use angularjs validation mechanisms every time you are about to move to the next step. This works because in the example we switch the content of the entire form every time we change the step on the form. Because of this, if you name a form for example "myMultiStepForm" you can use myMultiStepForm.$valid to see if the form is valid or not. So whenever the user is going to change the step you can call a function to check if the form is valid. For example:

Form with form name:

<form name="myMultiStepForm" id="signup-form" ng-submit="processForm()">
    <!-- our nested state views will be injected here -->
    <div id="form-views" ui-view></div>
</form>

Button to move to next section:

<a ng-click="goToNextSection(myMultiStepForm.$valid)" class="btn btn-block btn-info">
    Next Section <span class="glyphicon glyphicon-circle-arrow-right"></span>

Function to go to next section:

$scope.goToNextSection=function(isFormValid) {
      // If form is valid go to next section
      if(isFormValid) {
        $state.go(nextState($state.current.name));
      }
};

Extra notes to improve this and clarify the answer:

First of all it seems to me like you are starting a new project with forms so I would use the latest version of angularjs (at this time it is 1.3 release candidate 3) because it makes several improvements on how we can work with forms (for example it improves validation mechanisms). Since it is a release candidate they are mostly fixing bugs now and the API should be stable. You can read more about it here.

Here is the plunkr link with an example with validation http://plnkr.co/edit/y7XL9lGxB3wZTIAcxUVS?p=preview

This example is far from complete I just added validation to the email field and changed the behaviour of the 'next' button in order to illustrate checking the validation of the form. I use angularjs 1.3 validation (you can change it to 1.2 if you like but it will be a lot more logic), I added that validation to the email field and it only works for the first step. So if you try to click next you can't until you enter a valid email. Then you can click next and it will take you to the next one (that's where I stopped with the validation so the rest of the example is meaningless for now).

This is how I added email validation to the field:

<div class="form-group">
    <label for="email">Email</label>
    <input type="email" class="form-control" name="email" ng-model="formData.email" required>
    <div ng-messages="myMultiStepForm.email.$error" ng-if="formStepSubmitted || myMultiStepForm.email.$touched">
      <div ng-message="required">The email is required</div>
      <div ng-message="email">Not a valid email</div>
    </div>
</div>

The email is required and it has to be a valid email. Otherwise you get error message if you put an invalid value or try to submit the form.

To incorporate form validation into the process you just add the validation rules you want and for every call to the 'next step' button you validate the current version of the form and only advance if it is valid.

For this I changed the button to go to next step so it has an ng-click instead of ui-sref:

<a ng-click="goToNextSection(myMultiStepForm.$valid)" class="btn btn-block btn-info">
    Next Section <span class="glyphicon glyphicon-circle-arrow-right"></span>
</a>

Notice I also pass myMultiStepForm.$valid, myMultiStepForm is the name I gave to the form. $valid will be true if the form is valid and false otherwise.

Then in the controller I have a function that checks if the form is valid and only allows a change in state in that case:

$scope.goToNextSection=function(isFormValid) {
      // set to true to show all error messages (if there are any)
      $scope.formStepSubmitted = true;
      if(isFormValid) {
        // reset this for next form
        $scope.formStepSubmitted = false;

        // mark the step as valid so we can navigate to it via the links
        updateValidityOfCurrentStep(true /*valid */);

        // we only go to the next step if the form is valid
        $state.go(nextState($state.current.name));
      } else {
        // mark the step as valid so we can navigate to it via the links
        updateValidityOfCurrentStep(false /*not valid */);
      }
    };

Given that the user can use the links on the top to navigate you can also intercept the event of starting to change the state (stateChangeStart) and see if the previous step is valid and only allow the navigation if the previous step is valid.

.value('formSteps', [
  {uiSref: 'form.profile', valid: false},
  {uiSref: 'form.interests', valid: false},
  {uiSref: 'form.payment', valid: false}

  ])
.run([
            '$rootScope',
            '$state',
            'formSteps',
            function($rootScope, $state, formSteps) {

              // Register listener to watch route changes
                $rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {

                    var canGoToStep = false;
                    // only go to next if previous is valid
                    var toStateIndex = _.findIndex(formSteps, function(formStep) {
                      return formStep.uiSref === toState.name;

                    });

                    if(toStateIndex === 0) {
                      canGoToStep = true;
                    } else {
                      canGoToStep = formSteps[toStateIndex - 1].valid;
                    }

                    // Stop state changing if the previous state is invalid
                    if(!canGoToStep) {
                        // Abort going to step
                        event.preventDefault();
                    }
                });


            }


        ])

Notice I created a value called formSteps that is an array that has the validity of all the steps. That validity is updated in the controller whenever you click next step. You could also do it on change of the field value so that the validity of the form step does not depend on the user clicking next.


Need Your Help

Linker Error: Unresolved external (C++ Builder 6.0)

c++builder indy c++builder-6

I have a routine in C++ Builder 6 that send a file to webserver via HTTP Post, and when compile the project show these error messages from Linker:

Animated GIF won't stop looping Winforms

vb.net winforms visual-studio-2010

I have an animated gif in a PictureBox on my form but it is always looping, how can I make it only play once?