31

I'm trying to figure out reasonable approaches in AngularJS for creating a function that is composed of multiple steps (i.e., a wizard) but is linked to one page/URL. The data from one step would have to send data to (or share data with) the next step.

The main points are:

  • the url should remain the same (i.e., http://mydomain/myapp/nameupdater) for all of the steps and,
  • the data can be sent amongst steps (i.e., I have to give the data found from step 1 to populate the data in step 2).

For example, suppose that I have a function that does a bulk update of names:

  • In step 1 the function makes you search for a name.
  • In step 2 the function presents a list of names that were found from step 1 and allows the user to edit them.

I started an approach where each step had its own view and controller. And, the angular-ui-router maintained the states of the function. But, I have no idea how I would share the data between the steps.

Does anyone know of a good approach to establishing multi-step/wizard forms in angularjs?

My Plunker code is here of my very weak attempt at this.

whyceewhite
  • 6,317
  • 7
  • 43
  • 51
  • Read [this](http://stackoverflow.com/a/20029731/511374). – Sergey Moiseev Jun 14 '14 at 22:31
  • Might help to ask a specific question with code example instead of a broad one. – lucuma Jun 14 '14 at 22:42
  • 1
    @lucuma I agree. If I had a specific question in mind then I would pose it. But, I'm looking for an approach. I just don't know how a multi-step/wizard type situation is handled in angularjs without using different urls. – whyceewhite Jun 14 '14 at 22:51
  • 1
    You can easily maintain state data within one controller using a service or even just a simple variable. – lucuma Jun 14 '14 at 22:56
  • @lucuma Ok, I think I'm seeing how this should come together based on your input and [this example of a wizard](https://github.com/smsohan/angular_wizard). As you mentioned, _one_ controller makes more sense. I will try this. – whyceewhite Jun 14 '14 at 23:05
  • Simple example: http://plnkr.co/edit/g2T0r6MPzalkwADPen59?p=preview You can of course broadcast/emit changes (like the link you posted) but it doesn't have to be complicated. – lucuma Jun 14 '14 at 23:11
  • 1
    I also strongly recommend to use a single controller to put all steps together. You can outsource functionality in further services and directives to improve maintainability. – Alp Jun 15 '14 at 01:17
  • you can use a singleton model to hold the data from all steps which you need to submit normally at the last step: http://stackoverflow.com/questions/20930540/multiple-steps-form-wizard-with-ui-router – Elisabeth Jul 15 '14 at 20:28

1 Answers1

55

I think the best way of doing this would be to use ng-switch, just one controller, one route, no reload, using variables shared in all steps, like this:

<div ng-controller="stepCtrl">
   <div ng-switch="step">
      <div ng-switch-when="1">
         <!-- here you can include your step 1 template,
              or simply just hardcode it here: -->
         <div ng-include src="'.../step1.html'">
         <button ng-click="setStep(1)"></button>
      </div>
      <div ng-switch-when="2">

         <div ng-include src="'.../step2.html'">
         <button ng-click="setStep(2)"></button>
      </div>
      <div ng-switch-when="3">
         <div ng-include src="'.../step3.html'">
         <button ng-click="setStep(3)"></button>
      </div>
   </div>
</div>

     yourApp.controller('stepCtrl',function($scope){
        $scope.step = 1;
        $scope.setStep = function(step){
           $scope.step = step;
        }
      });

This way you can also manipulate the URL to add a step at the end of your current location.

UPDATE :

Actually this answer is for long time ago , this days I personally prefer to use ui-router which is a great module which you can inject to your AngularJs application and make it even more cool with nested views . Speaking of nested views , bellow is my new approach for a multystep form with some animation :

First :

Using $stateProvider declare any steps you want in separate views : 

 app.config(function($stateProvider, $urlRouterProvider) {

$stateProvider

    .state('wizard', {// this will be the wrapper for our wizard
        url: '/wizard',
        templateUrl: 'wizard.html',
        controller: 'wizardController'
    })
    .state('wizard.stepOne', {// this will be the wrapper for our wizard
        url: '/stepOne',
        templateUrl: 'stepOne.html',
        controller: 'wizardController'
    })
    .state('wizard.stepTwo', {// this will be the wrapper for our wizard
        url: '/stepTwo',
        templateUrl: 'stepTwo.html',
        controller: 'wizardController'
    })

Then later in our "wizard.html" we can have something like this :

    <div id="container">

    <div>
        <h2>Our multistep form wizard</h2>


        <div id="status-buttons" class="text-center">
            <a ui-sref-active="active" ui-sref=".stepOne"><span>1</span> Step One</a>
            <a ui-sref-active="active" ui-sref=".stepTwo"><span>2</span> Step Two </a>

        </div>
    </div>
   <!-- Here we specify our view that is a container for our subviews(steps) , which in our case can be a form !-->

    <form id="signup-form" ng-submit="submit()">
        <!-- nested state views will be inserted here -->
        <div  ui-view></div>
    </form>

</div>

And obviously for our steps , we must have seperated html files. This way , we still have one controller , url will be updated , and we can also add angular animation .

Milad
  • 27,506
  • 11
  • 76
  • 85
  • 1
    This worked for me and it was a clear simple solution. Thanks. – whyceewhite Jun 16 '14 at 00:32
  • 3
    This is an excellent and simple solution to a common need. However, be aware that ng-switch creates its own scope and this may cause some problems. Please see this for more details: http://stackoverflow.com/questions/15593299/angularjs-ng-switch-does-not-bind-ng-model – RajV Oct 06 '14 at 22:18
  • 5
    xe4me one controller makes no sense. Where do you put the ui logic and interaction with the html controls? All in the one controller? surely not... Where do you put your 3 isValid function having 3 steps all in one controller? Surely not possible due to duplicate. One WizardController drives the wizard general logic and each step has its own controller where the individual ui interaction is done. – Elisabeth Oct 18 '14 at 09:38
  • But isn't the problem here is that the data won't be sticky if they navigate from one step to the next? I am going to try multiple ui-router views which are shown / hidden depending on which step they are on. That way the user's choices remain in the dom instead of blown away. – httpete May 11 '16 at 20:41
  • @httpete There are numerous ways to share data/state across controllers. Each separate step controller would simply add its focused data to a shared state object and the DOM would be redrawn when moving from step to step, from data within that shared state object. – TSmith Aug 18 '16 at 14:07