Skip to content Skip to sidebar Skip to footer

Logic For The Next Button For The Questionnaire?

I am beginner in AngularJS and facing some issues. I am trying to make a questionnaire having 1 question on each page. On every Next button data is save in the database. I am tryin

Solution 1:

You could create a multi-step form (also known as Wizard). For this purpose why not using Angular ui-router as suggested in this post:

https://scotch.io/tutorials/angularjs-multi-step-form-using-ui-router

which is a tutorial guiding to create a Wizard and has a working example (a Plunker).

There are also other Angular modules which aim to do the same:

And in conclusion another tutorial with working demo:

But if you "google" for "multi-step form angular" you can find some more examples.

Also on StackOverflow you can find suggestions on how to create a multi-step form (for example see AngularJS multi-step form validation).


Solution 2:

You could save your showDiv booleans in an array, and keep the corresponding index in memory. So something like this:

$scope.currentState = 0;

//ordered representation of possible states
$scope.stateArray = [true, false, false, false, false];

$scope.next = function(){

  //on to the next showDiv
  currentState++;

  stateArray.forEach(function(index, state){

    //hide everything else.
    if(index != currentState){
      state = false;
    }
    else{
      state = true;
    }
  }
}

Then, in your html, do ng-show="stateArray[currentState]"

You'll inadvertently gain a "previous" function from this if you choose by decrementing the currentState instead.


Solution 3:

I would create three custom directives that can be used to build a multi-part form:

  1. myMultiPartForm directive would wrap the form and keep track of which part is visible
  2. myFormPart directive would be used multiple times as a wrapper for each form section
  3. myFormPartSubmit would be used on the submit button in each form part, to advance to the next part

Here is a working example: JSFiddle

Example

index.html

In this file, I am using MyViewController as the view, which will expose a part variable for keeping track of which form part is currently being displayed, as well as a save method that each form part can call when it is submitted.

<div ng-app="myApp" ng-controller="MyViewController as view">
  <my-multi-part-form part="{{view.part}}">
    <my-form-part on-submit="view.save(data)">
      <label>
        Name:
        <input type="text" ng-model="formPart.data.name">
      </label>
      <button my-form-part-submit>
        Next
      </button>
    </my-form-part>
    <my-form-part on-submit="view.save(data)">
      <label>
        Age:
        <input type="number" ng-model="formPart.data.age">
      </label>
      <button my-form-part-submit>
        Next
      </button>
    </my-form-part>
    <my-form-part on-submit="view.save(data)">
      <label>
        Gender:
        <select ng-model="formPart.data.gender">
          <option value="male">Male</option>
          <option value="female">Female</option>
        </select>
      </label>
      <button my-form-part-submit>
        Done
      </button>
    </my-form-part>
  </my-multi-part-form>
  <div ng-if="view.part > 2">
    Complete!
  </div>
</div>

view.controller.js

The view controller initializes the part variable at zero, which is the index of the first form part (form parts are stored in an array, in MultiPartFormController).

angular.module('myApp')
  .controller('MyViewController', MyViewController)
;

function MyViewController($http) {
  var view = this;
  view.part = 0;
  view.save = function(data) {
    $http({
      method : 'POST',
      url    : 'https://example.com',
      data   : data
    }).then(function success(res) {
      /** handle success **/
      view.part++;
    }).catch(function error(err) {
      /** handle error **/
    });
  };
}

multi-part-form.directive.js

Here, I define the myMultiPartForm directive and observe the part attribute, which is the interpolated value of view.part. Whenever that value changes (i.e. on success after view.save is called), it will hide all form parts except the one that view.part now references.

angular.module('myApp')
  .directive('myMultiPartForm', myMultiPartFormDirective)
  .controller('MultiPartFormController', MultiPartFormController)
;

function myMultiPartFormDirective() {
  return {
    controller  : 'MultiPartFormController',
    controllerAs: 'multiPartForm',
    link        : postLink
  };

  function postLink(scope, iElement, iAttrs, multiPartForm) {
    iAttrs.$observe('part', function (newValue) {
      angular.forEach(multiPartForm.parts, function (part, index) {
        if (index == newValue) part.show();
        else part.hide();
      });
    });
  }
}

function MultiPartFormController() {
  var multiPartForm = this;
  multiPartForm.parts = [];
}

form-part.directive.js

Here's where it gets cool. Each myFormPart directive adds show and hide methods to its controller during the post-link phase, then adds a reference to its controller to the parts array of the myMultiPartForm controller. This enables myMultiPartForm to manipulate the DOM element of each myFormPart without needing to traverse the DOM tree using jQuery or jqLite.

angular.module('myApp')
  .directive('myFormPart', myFormPartDirective)
  .controller('FormPartController', FormPartController)
;

function myFormPartDirective() {
  return {
    bindToController: {
      onSubmit: '&'
    },
    controller      : 'FormPartController',
    controllerAs    : 'formPart',
    link            : postLink,
    require         : ['myFormPart', '^myMultiPartForm'],
    scope           : true,
    template        : '<ng-transclude></ng-transclude>',
    transclude      : true
  };

  function postLink(scope, iElement, iAttrs, controllers) {
    var formPart = controllers[0];
    var multiPartForm = controllers[1];
    formPart.hide = function () {
      iElement.css({display: 'none'});
    };
    formPart.show = function () {
      iElement.css({display: 'block'});
    };
    multiPartForm.parts.push(formPart);
  }
}

function FormPartController() {
  var formPart = this;
  formPart.data = {};
}

form-part-submit.directive.js

Finally, this directive adds a click handler to whatever element it is applied to that will call myFormPart.onSubmit, which in this example is always the view.save method (but could be a different function for each form part).

angular.module('myApp')
  .directive('myFormPartSubmit', myFormPartSubmitDirective)
;

function myFormPartSubmitDirective() {
  return {
    link: postLink,
    require: '^myFormPart'
  };

  function postLink(scope, iElement, iAttrs, formPart) {
    iElement.on('click', function() {
      if (typeof formPart.onSubmit === 'function') {
        formPart.onSubmit({data: formPart.data});
      }
    });
  }
}

Order of Operations

To understand how all of this works, you need to understand the order in which things happen. Here is an outline:

  1. multiPartForm controller instantiated
  2. formPart A controller instantiated
  3. formPartSubmit A DOM element linked
  4. formPart A DOM element linked
  5. formPart B controller instantiated
  6. formPartSubmit B DOM element linked
  7. formPart B DOM element linked
  8. formPart C controller instantiated
  9. formPartSubmit C DOM element linked
  10. formPart C DOM element linked
  11. multiPartForm DOM element linked

The multiPartForm controller is instantiated first, but linked last. That means by the time its postLink function is called, its controller has all of the information it needs about each formPart. Then, the part value gets interpolated and the first $observe callback is fired.


Post a Comment for "Logic For The Next Button For The Questionnaire?"