Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS multi-step form validation using ui-router module

I have a form that is spread over multiple tabs. Originally, I used the tabset directive from ui-bootstrap, but then came the requirement to deep link to a specific tab, so I thought I'd use nested views from ui-router.

The problem I have is that the parent form is only valid when all the sub-forms are valid, but using ui-router states, only one sub-form is loaded at a time.

Here's an example to clarify

index.html

<!DOCTYPE html>
<html ng-app="app">
<head>
  <script src="//code.angularjs.org/1.3.0-beta.5/angular.js"></script>
  <script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.10/angular-ui-router.js"></script>
  <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" />
  <script src="script.js"></script>
</head>
<body class="container-fluid">
  <div ui-view>
    <ul class="list-unstyled">
      <li><a ui-sref="edit.basic">Basic</a></li>
      <li><a ui-sref="edit.extended">Extended</a></li>
    </ul>
  </div>
</body>
</html>

script.js

angular.module('app', ['ui.router']).
config(function($stateProvider) {
  $stateProvider.state('edit', {
    abstract: true,
    url: '/edit/',
    templateUrl: 'edit.html',
    controller: function($scope) {
      $scope.model = {};
    }
  }).
  state('edit.basic', {
    url: '/basic',
    templateUrl: 'basic.html'
  }).
  state('edit.extended', {
    url: '/extended',
    templateUrl: 'extended.html'
  });
});

edit.html

<div class="row" ng-form="editForm">
  <div class="col-xs-12">
    <ul class="nav nav-tabs">
      <li ui-sref-active="active">
        <a ui-sref="edit.basic">Basic</a>
      </li>
      <li ui-sref-active="active">
        <a ui-sref="edit.extended">Extended</a>
      </li>
    </ul>
  </div>
  <div class="col-xs-12" ui-view></div>
  <div class="col-xs-12">
    <button type="button" class="btn btn-primary" ng-disabled="editForm.$invalid">Save</button>
  </div>
  <div class="col-xs-12">
    <hr>
    <tt>model = {{model}}</tt><br>
    <tt>editForm.$valid = {{editForm.$valid}}</tt><br>
    <tt>editForm.basicForm.$valid = {{editForm.basicForm.$valid}}</tt><br>
    <tt>editForm.extendedForm.$valid = {{editForm.extendedForm.$valid}}</tt>
  </div>
</div>

basic.html

<div ng-form="basicForm">
  <div class="form-group">
    <label for="textProperty">Text Property</label>
    <input type="text" class="form-control" name="textProperty" id="textProperty" ng-model="model.textProperty" required>
  </div>
</div>

extended.html

<div ng-form="extendedForm">
  <div class="form-group">
    <label for="numericProperty">Numeric Property</label>
    <input type="number" class="form-control" name="numericProperty" id="numericProperty" ng-model="model.numericProperty" required>
  </div>
  <div class="form-group">
    <label for="dateProperty">Date Property</label>
    <input type="date" class="form-control" name="dateProperty" id="dateProperty" ng-model="model.dateProperty" required>
  </div>
</div>

I am beginning to believe that this approach is not suitable for my purpose. Instead of using nested views, I should continue to use the tabset and use a state parameter to activate the appropriate tab.

I'm interested to know how others would solve it.

UPDATE 1:

Here is one solution I came up with that uses the ui-bootstrap tabset but doesn't use nested ui-router states and instead parameterises the active tab.

UPDATE 2:

Here is a solution which uses nested states along with a validator service following the suggestion of Santiago Rebella

UPDATE 3:

Here is an alternate solution to update 2 which uses a validator object on the parent state's scope for comparison

like image 883
gwhn Avatar asked Aug 26 '14 16:08

gwhn


1 Answers

You could try to do it in the way you state just passing to a service some kind of attributes like step1, step2, etc and go adding true or whatever your success value you may use, so once all those attributes are true, in your ng-disabled or the way you are building your form, is allowed to be submitted.

Maybe previous formsteps you could be storing the values of the inputs in an object in your service. I think in this way you could a multi-page form.

like image 162
Santiago Rebella Avatar answered Nov 14 '22 02:11

Santiago Rebella