Wizard

Wizards are used on a modal form when the user needs to perform a set of pre-defined steps in a particular order. The Sky UX wizard works in conjunction with the Angular UI Bootstrap tabs component. Placing the bb-wizard directive on a UI Bootstrap uib-tabset element will cause the tabs to look and behave like a Sky wizard.

Sky wizards also have the concept of a completed step which is denoted by the bb-wizard-step-complete directive. When present on a uib-tab and bound to a truthy value, the step's tab will be displayed as completed.

Finally there is a bbWizardNavigator service that provides some convenience methods for navigating through the wizard's steps. This will typically be used by wiring the navigator up to your modal's previous and next buttons.

The bbWizardNavigator service has an init() function that takes an options object with the following properties:

  • active — The index of the active tab, this should be the same property that is bound the the UI Bootstrap uib-tabset directive's active property.
  • steps — An array of steps. Each step should have the following properties:
    • active(Deprecated.) Use the UI Bootstrap uib-tabset directive's active property instead Indicates whether the step is the currently active step. This should be the same property that is bound to the UI Bootstrap uib-tab directive's active property.
    • disabled() — A function that returns a boolean indicating whether the tab is disabled. This should be the same function that is bound to the UI Bootstrap uib-tab directive's disable property.
    • complete() — A function that returns a boolean indicating whether the tab is complete. This should be the same function that is bound to the tab's bb-wizard-step-complete property.
  • finish() — A function that will execute when the user clicks finish on the last step of the wizard.

The bbWizardNavigator also exposes the following methods:

  • previousText() — Returns the text for the modal's Previous button. This usually doesn't change while the user interacts with the widget.
  • nextText() — Returns the text for the modal's Next button. This changes to "Finish" when the user is on the last step.
  • goToPrevious() — Navigates the user to the previous step.
  • goToNext() Navigates the user to the next step.
  • previousDisabled() — Indicates whether the previous step is disabled. This should be bound to the ng-disabled property of the modal's Previous button.
  • nextDisabled() — Indicates whether the next step is disabled. This should be bound to the ng-disabled property of the modal's Next button.

Demo

Markup

<div ng-controller="WizardTestController as wizardCtrl">
  <button class="btn btn-primary" ng-click="wizardCtrl.openForm()">Open wizard</button>
</div>

<script type="text/ng-template" id="demo/wizard/wizardform.html">
  <bb-modal>
    <div class="modal-form" ng-controller="WizardTestModalController as wizardModalCtrl">
      <bb-modal-header>Wizard example</bb-modal-header>
      <form ng-submit="wizardModalCtrl.save()">
        <div bb-modal-body>
          <uib-tabset bb-wizard active="wizardModalCtrl.options.active">
            <uib-tab ng-repeat="step in wizardModalCtrl.steps" bb-scroll-into-view="wizardModalCtrl.options.active === $index" heading="{{step.heading}}" bb-tab-heading-xs="{{step.heading_xs}}" disable="step.disabled()" bb-wizard-step-complete="step.complete()">
              <ng-include src="step.templateUrl"></ng-include>
            </uib-tab>
          </uib-tabset>
        </div>
        <bb-modal-footer>
          <bb-modal-footer-button ng-click="wizardModalCtrl.wizardNav.goToPrevious()" ng-disabled="wizardModalCtrl.wizardNav.previousDisabled()">{{wizardModalCtrl.wizardNav.previousText()}}</bb-modal-footer-button>
          <bb-modal-footer-button-primary ng-click="wizardModalCtrl.wizardNav.goToNext()" ng-disabled="wizardModalCtrl.wizardNav.nextDisabled()">{{wizardModalCtrl.wizardNav.nextText()}}</bb-modal-footer-button-primary>
          <bb-modal-footer-button>Save and Close</bb-modal-footer-button>
          <bb-modal-footer-button-cancel></bb-modal-footer-button-cancel>
        </bb-modal-footer>
      </form>
    </div>
  </bb-modal>
</script>

<script type="text/ng-template" id="demo/wizard/step1.html">
  <div class=" form-group">
    <label>Enter text here to continue.</label>
    <input type="text" class="form-control" ng-model="wizardModalCtrl.requiredValue1" />
  </div>
</script>

<script type="text/ng-template" id="demo/wizard/step2.html">
  <div class=" form-group">
    <label>
      <input type="checkbox" bb-check ng-model="wizardModalCtrl.requiredValue2" />
      Check this box to enable step 3.
    </label>
  </div>
</script>

<script type="text/ng-template" id="demo/wizard/step3.html">
  <div class=" form-group">
    Some other content.
  </div>
</script>

JavaScript

/*global angular */
(function () {
    'use strict';

    function WizardTestController(bbModal) {
        var self = this;

        self.openForm = function () {
            bbModal.open({
                templateUrl: 'demo/wizard/wizardform.html'
            });
        };
    }

    function WizardTestModalController($window, bbWizardNavigator) {
        var self = this,
            steps,
            wizardNav;

        steps = [
            {
                heading: '1. Step 1',
                heading_xs: '1',
                templateUrl: 'demo/wizard/step1.html',
                complete: function () {
                    return !!self.requiredValue1;
                }
            },
            {
                heading: '2. Step 2',
                heading_xs: '2',
                templateUrl: 'demo/wizard/step2.html',
                complete: function () {
                    return !!self.requiredValue2;
                },
                disabled: function () {
                    return !self.requiredValue1;
                }
            },
            {
                heading: '3. Step 3',
                heading_xs: '3',
                templateUrl: 'demo/wizard/step3.html',
                complete: function () {
                    return !!self.requiredValue3;
                },
                disabled: function () {
                    return !self.requiredValue1 || !self.requiredValue2;
                }
            }
        ];

        self.options = {
            steps: steps,
            finish: function () {
                $window.alert('Finished!');
            },
            active: 0
        };

        wizardNav = bbWizardNavigator.init(
            self.options
        );

        self.steps = steps;

        self.wizardNav = wizardNav;

        self.firstStepComplete = function () {
            return !!self.requiredValue1;
        };

        self.secondStepComplete = function () {
            return !!self.requiredValue2;
        };

    }

    WizardTestController.$inject = ['bbModal'];

    WizardTestModalController.$inject = ['$window', 'bbWizardNavigator'];

    angular.module('stache')
        .controller('WizardTestController', WizardTestController)
        .controller('WizardTestModalController', WizardTestModalController);

}());