horizon/horizon/static/framework/widgets/wizard/wizard.controller.js

237 lines
6.9 KiB
JavaScript

/*
* (c) Copyright 2015 Hewlett-Packard Development Company, L.P.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
(function() {
'use strict';
var extend = angular.extend;
var forEach = angular.forEach;
angular
.module('horizon.framework.widgets.wizard')
.controller('WizardController', WizardController);
WizardController.$inject = [
'$scope',
'$q',
'horizon.framework.widgets.wizard.labels',
'horizon.framework.widgets.wizard.events',
'horizon.framework.events'
];
/**
* @ngdoc controller
* @name horizon.framework.widgets.wizard.controller:WizardController
* @description
* Controller used by 'wizard'
*/
function WizardController($scope, $q, wizardLabels, wizardEvents, frameworkEvents) {
var ctrl = this;
var viewModel = $scope.viewModel = {};
var initTask = $q.defer();
/*eslint-disable angular/controller-as */
$scope.initPromise = initTask.promise;
$scope.currentIndex = -1;
$scope.workflow = $scope.workflow || {};
if ($scope.workflow.initControllers) {
$scope.workflow.initControllers($scope);
}
var steps = $scope.steps = $scope.workflow.steps || [];
$scope.wizardForm = {};
// a place to keep each step's captured data, named for their step.formName
$scope.stepModels = {};
$scope.switchTo = switchTo;
$scope.showError = showError;
ctrl.toggleHelpBtn = toggleHelpBtn;
ctrl.onInitSuccess = onInitSuccess;
ctrl.onInitError = onInitError;
/*eslint-enable angular/controller-as */
viewModel.btnText = extend({}, wizardLabels, $scope.workflow.btnText);
viewModel.btnIcon = $scope.workflow.btnIcon || {};
viewModel.showSpinner = false;
viewModel.hasError = false;
viewModel.onClickFinishBtn = onClickFinishBtn;
viewModel.isSubmitting = false;
$scope.initPromise.then(onInitSuccess, onInitError);
checkAllReadiness().finally(function() {
initTask.resolve();
viewModel.ready = true;
switchToFirstReadyStep();
});
//////////
function switchTo(index) {
/**
* In each step's controller, $scope.$index can be used by the step
* to identify itself. For example:
*
* var comingToMe = (event.to === $scope.$index);
*/
$scope.$broadcast(wizardEvents.ON_SWITCH, {
from: $scope.currentIndex,
to: index
});
ctrl.toggleHelpBtn(index);
/*eslint-disable angular/controller-as */
$scope.currentIndex = index;
$scope.openHelp = false;
/*eslint-enable angular/controller-as*/
}
function showError(errorMessage) {
viewModel.showSpinner = false;
viewModel.hasError = true;
if (errorMessage && angular.isString(errorMessage.data)) {
viewModel.errorMessage = errorMessage.data;
} else {
viewModel.errorMessage = errorMessage;
}
viewModel.isSubmitting = false;
}
function beforeSubmit() {
$scope.$broadcast(wizardEvents.BEFORE_SUBMIT);
}
function afterSubmit(args) {
$scope.$broadcast(wizardEvents.AFTER_SUBMIT);
/*eslint-disable angular/controller-as */
$scope.close(args);
/*eslint-enable angular/controller-as */
}
function onClickFinishBtn() {
// prevent the finish button from being clicked again
viewModel.isSubmitting = true;
beforeSubmit();
$scope.submit($scope.stepModels).then(afterSubmit, showError);
}
function onInitSuccess() {
if (viewModel.hasError) {
return;
}
$scope.$broadcast(wizardEvents.ON_INIT_SUCCESS);
if (steps.length > 0) {
ctrl.toggleHelpBtn(0);
}
}
function onInitError() {
$scope.$broadcast(wizardEvents.ON_INIT_ERROR);
}
$scope.$on(frameworkEvents.AUTH_ERROR, function(evt, arg) {
viewModel.hasError = true;
viewModel.errorMessage = arg;
return;
});
function toggleHelpBtn(index) {
// Toggle help icon button if a step's helpUrl is not defined
if (angular.isUndefined(steps[index].helpUrl)) {
$scope.hideHelpBtn = true;
} else {
$scope.hideHelpBtn = false;
}
}
/**
* Each step in the workflow can provide an optional `checkReadiness`
* method, which should return a promise. When this method is provided
* with a step, the `.ready` property of the step will be set to
* `false` until the promise gets resolved. If no `checkReadiness` method
* is provided, the `.ready` property of the step will be set to `true`
* by default.
*
* This is useful for workflows where some steps are optional and/or
* displayed to the UI conditionally, and the check for the condition
* is an asynchronous operation.
*
* @return {Promise} This promise gets resolved when all the checking
* for each step's promises are done.
*
* @example
```js
var launchInstanceWorkFlow = {
//...
steps: [
// ...
{
title: gettext('Network'),
templateUrl: path + 'launch-instance/network/network.html',
helpUrl: path + 'launch-instance/network/network.help.html',
formName: 'launchInstanceNetworkForm',
checkReadiness: function() {
var d = $q.defer();
setTimeout(function() {
d.resolve();
}, 500);
return d.promise;
}
}
//...
],
//...
};
```
*/
function checkAllReadiness() {
var stepReadyPromises = [];
forEach(steps, function(step) {
step.ready = !step.checkReadiness;
if (step.checkReadiness) {
var promise = step.checkReadiness();
stepReadyPromises.push(promise);
promise.then(function() {
step.ready = true;
});
}
});
viewModel.ready = stepReadyPromises.length === 0;
return $q.all(stepReadyPromises).finally(function() {
$scope.steps = $scope.steps.filter(function(step) {
return step.ready;
});
});
}
function switchToFirstReadyStep() {
forEach(steps, function (step, index) {
/*eslint-disable angular/controller-as */
if ($scope.currentIndex < 0 && step.ready) {
$scope.currentIndex = index;
return;
}
/*eslint-enable angular/controller-as */
});
}
}
})();