10

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

2 Answers 2

5

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for your suggestion. I have implemented an example using a service to manage the validation state and updated the question with a link to it.
0

controller.js

var app = angular.module('employeeDemoApp');
app.controller('employeesCtrl', function($scope, $state, EmployeeService, $stateParams) {
    console.log("Hello")
        $scope.saveEmployee = function(formname){
            console.log(formname.$error)
            EmployeeService.saveEmployee($scope.user);
            $state.go("employees");
        }
        $scope.employeeslist = EmployeeService.getEmployees();
        if($stateParams && $stateParams.id){
            $scope.user = EmployeeService.getEmployee($stateParams.id);
            $scope.user.dob = new Date($scope.user.dob);
            console.log("gdfg", $scope.user)
        }
        $scope.updateEmployee = function(req){
          EmployeeService.updateEmployee($stateParams.id, $scope.user);
          $state.go("employees")
        }
        $scope.goto_new_employee_page = function(){
        $state.go("employees_new")
    }
        $scope.deleteEmployee = function(index){
            console.log("fgdfhdh")
            EmployeeService.deleteEmployee(index);
             $scope.employeeslist = EmployeeService.getEmployees();
        } 
});

1 Comment

Whenever you are going to post an answer, explain your code as well.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.