From 421cdbce249feabf7e28e1c1bd0aebfcfd9b543d Mon Sep 17 00:00:00 2001 From: Thai Tran Date: Thu, 18 Feb 2016 12:01:04 -0800 Subject: [PATCH] Tutorial for extending a workflow Added tutorial for extending an existing workflow as a plugin. Change-Id: I8e6af24b9d1dfe313e6ee6d43894d87d9c97068 Closes-Bug: #1511593 --- doc/source/tutorials/workflow_extend.rst | 202 +++++++++++++++++++++++ 1 file changed, 202 insertions(+) create mode 100644 doc/source/tutorials/workflow_extend.rst diff --git a/doc/source/tutorials/workflow_extend.rst b/doc/source/tutorials/workflow_extend.rst new file mode 100644 index 0000000000..3777a4af49 --- /dev/null +++ b/doc/source/tutorials/workflow_extend.rst @@ -0,0 +1,202 @@ +Extending an AngularJS Workflow +=============================== + +A workflow extends the ``extensibleService``. This means that all workflows +inherit properties and methods provided by the ``extensibleService``. Extending +a workflow allows you to add your own steps, remove existing steps, and inject +custom data handling logic. Refer to inline documentation on what those +properties and methods are. + +We highly recommend that you complete the +:doc:``plugin tutorial `` if you have not done so already. +If you do not know how to package and install a plugin, the rest of this +tutorial will not make sense! In this tutorial, we will examine an existing +workflow and how we can extend it as a plugin. + +.. Note :: + + Although this tutorial focuses on extending a workflow, the steps here + can easily be adapted to extend any service that inherited the + ``extensibleService``. Examples of other extensible points include + table columns and table actions. + +File Structure +-------------- + +Remember that the goal of this tutorial is to inject our custom step into an +**existing** workflow. All of the files we are interested in reside in the +``static`` folder. + + myplugin + │ + ├── enabled + │ └── _31000_myplugin.py + │ + └── static + └── horizon + └── app + └── core + └── images + ├── plugins + │ └── myplugin.module.js + │ + └── steps + └── mystep + ├── mystep.controller.js + ├── mystep.help.html + └── mystep.html + +myplugin.module.js +------------------ + +This is the entry point into our plugin. We hook into an existing module via the +run block which is executed after the module has been initialized. All we need +to do is inject it as a dependency and then use the methods provided in the +extensible service to override or modify steps. In this example, we are going to +prepend our custom step so that it will show up as the first step in the wizard. + +:: +(function () { + 'use strict'; + + angular + .module('horizon.app.core.images') + .run(myPlugin); + + myPlugin.$inject = [ + 'horizon.app.core.images.basePath', + 'horizon.app.core.images.workflows.create-volume.service' + ]; + + function myPlugin(basePath, workflow) { + var customStep = { + id: 'mypluginstep', + title: gettext('My Step'), + templateUrl: basePath + 'steps/mystep/mystep.html', + helpUrl: basePath + 'steps/mystep/mystep.help.html', + formName: 'myStepForm' + }; + workflow.prepend(customStep); + } + +})(); + +.. Note :: + + Replace ``horizon.app.core.images.workflows.create-volume.service`` with + the workflow you intend to augment. + +mystep.controller.js +-------------------- + +It is important to note that the scope is the glue between our controllers, +this is how we are propagating events from one controller to another. We can +propagate events upward using the $emit method and propagate events downward +using the $broadcast method. + +Using the $on method, we can listen to events generated within the scope. In +this manner, actions we completed in the wizard are visually reflected in the +table even though they are two completely different widgets. Similarly, you can +share data between steps in your workflow as long as they share the same parent +scope. + +In this example, we are listening for events generated by the wizard and the +user panel. We also emit a custom event that other controllers can register to +when favorite color changes. + +:: + (function() { + 'use strict'; + + angular + .module('horizon.app.core.images') + .controller('horizon.app.core.images.steps.myStepController', + myStepController); + + myStepController.$inject = [ + '$scope', + 'horizon.framework.widgets.wizard.events', + 'horizon.app.core.images.events' + ]; + + function myStepController($scope, wizardEvents, imageEvents) { + + var ctrl = this; + ctrl.favoriteColor = 'red'; + + /////////////////////////// + + $scope.$on(wizardEvents.ON_SWITCH, function(e, args) { + console.info('Wizard is switching step!'); + console.info(args); + }); + + $scope.$on(wizardEvents.BEFORE_SUBMIT, function() { + console.info('About to submit!'); + }); + + $scope.$on(imageEvents.VOLUME_CHANGED, function(event, newVolume) { + console.info(newVolume); + }); + + /////////////////////////// + + $scope.$watchCollection(getFavoriteColor, watchFavoriteColor); + + function getFavoriteColor() { + return ctrl.favoriteColor; + } + + function watchFavoriteColor(newColor, oldColor) { + if (newColor != oldColor) { + $scope.$emit('mystep.favoriteColor', newColor); + } + } + } + + })(); + +mystep.help.html +---------------- + +In this tutorial, we will leave this file blank. Include additional information +here if your step requires it. Otherwise, remove the file and the ``helpUrl`` +property from your step. + +mystep.html +----------- + +This file contains contents you want to display to the user. We will provide a +simple example of a step that asks for your favorite color. The most important +thing to note here is the reference to our controller via the ``ng-controller`` +directive. This is essentially the link to our controller. + +:: +
+

Blue Plugin

+
+
My custom step
+
+ Place your custom content here! +
+
+
+
+
+ + +
+
+
+
+
+
+ +Testing +------- + +Now that we have completed our plugin, lets package it and test that it works. +If you need a refresher, take a look at the installation section in +:doc:`Plugin Tutorial `.