Repository: ansible/awx
Pull request: Implemented new workflow node prompting
##### SUMMARY
Workflow node prompting exists currently but is a much different user experience from the new launch/schedule prompting.  This PR brings workflow node creation/editing in line with schedule prompting.

Part of primary feature dev for https://github.com/ansible/awx/issues/169
Should also fix https://github.com/ansible/awx/issues/1477 and https://github.com/ansible/awx/issues/1480

##### ISSUE TYPE
 - Feature Pull Request

##### COMPONENT NAME
 - UI

##### AWX VERSION
```
awx: 1.0.4.65
```


##### ADDITIONAL INFORMATION
When the user selects a Job Template with promptable fields on a workflow job template node, they will now see a PROMPT button appear beneath the edge type dropdown:

<img width="400" alt="screen shot 2018-03-08 at 6 49 21 pm" src="https://user-images.githubusercontent.com/9889020/37182879-71772994-2301-11e8-9523-8f47fdd8f127.png">

If the Job Template does not have a default inventory then the SAVE button will be disabled until they click on the PROMPT button and select an inventory to use.  Previously, a user was not permitted to select a Job Template without a default inventory but this restriction has been removed.


Problem statement:
Console traceback; 'replace' undefined when launching prompt for extra vars JT
##### ISSUE TYPE
<!--- Pick one below and delete the rest: -->
 - Bug Report

##### COMPONENT NAME
<!-- Pick the area of AWX for this issue, you can have multiple, delete the rest: -->
 - UI

##### SUMMARY
Console traceback when launching JT with prompt for extra vars.

##### STEPS TO REPRODUCE

* Create a JT with prompt for extra vars on launch
* click the Rocket !!!!

##### EXPECTED RESULTS

* No Traceback in the console

##### ACTUAL RESULTS

traceback in the console

##### ADDITIONAL INFORMATION

```
TypeError: Cannot read property 'replace' of undefined
    at fn.showTextArea (AngularCodeMirror.js:61)
    at createField (parse-type-change.factory.js:54)
    at parse-type-change.factory.js:66
    at codemirrorExtraVars (prompt-other-prompts.controller.js:33)
    at exports.default.vm.init (prompt-other-prompts.controller.js:72)
    at Object.link (prompt-other-prompts.directive.js:29)
    at angular.js:1383
    at angular.js:10613
    at invokeLinkFn (angular.js:10619)
    at nodeLinkFn (angular.js:10008) "<form name="otherPromptsForm" autocomplete="off" novalidate="" prompt-data="vm.promptData" other-prompts-form="vm.forms.otherPrompts" is-active-step="vm.steps.other_prompts.tab._active" class="ng-pristine ng-valid ng-isolate-scope">"
```

Hints from issue discussion:

Commit message: Fixed several workflow prompting and edge type bugs

Patch to review:
diff --git a/awx/ui/client/src/scheduler/schedulerEdit.controller.js b/awx/ui/client/src/scheduler/schedulerEdit.controller.js
index c73d7739c093..9e6c54fb7cce 100644
--- a/awx/ui/client/src/scheduler/schedulerEdit.controller.js
+++ b/awx/ui/client/src/scheduler/schedulerEdit.controller.js
@@ -349,7 +349,7 @@ function($filter, $state, $stateParams, Wait, $scope, moment,
 
                                         let processed = PromptService.processSurveyQuestions({
                                             surveyQuestions: surveyQuestionRes.data.spec,
-                                            extra_data: data.extra_data
+                                            extra_data: _.cloneDeep(data.extra_data)
                                         });
 
                                         $scope.missingSurveyValue = processed.missingSurveyValue;
diff --git a/awx/ui/client/src/templates/main.js b/awx/ui/client/src/templates/main.js
index 144d597e2ec5..456c37fc7826 100644
--- a/awx/ui/client/src/templates/main.js
+++ b/awx/ui/client/src/templates/main.js
@@ -37,7 +37,7 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p
     .config(['$stateProvider', 'stateDefinitionsProvider', '$stateExtenderProvider',
         function($stateProvider, stateDefinitionsProvider, $stateExtenderProvider) {
             let stateTree, addJobTemplate, editJobTemplate, addWorkflow, editWorkflow,
-                workflowMaker, inventoryLookup, credentialLookup,
+                workflowMaker,
                 stateDefinitions = stateDefinitionsProvider.$get(),
                 stateExtender = $stateExtenderProvider.$get();
 
@@ -401,8 +401,7 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p
                         job_template_search: {
                             value: {
                                 page_size: '5',
-                                order_by: 'name',
-                                inventory__isnull: false
+                                order_by: 'name'
                             },
                             squash: false,
                             dynamic: true
@@ -474,30 +473,29 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p
                                                     name: row.name
                                                 };
 
-                                                $scope.templateSelected(row);
+                                                $scope.templateManuallySelected(row);
                                             }
                                         });
 
                                     };
 
-                                    $scope.$on('templateSelected', function(e, options) {
-                                        if(options.activeTab !== 'jobs') {
+                                    $scope.$watch('selectedTemplate', () => {
+                                        $scope.job_templates.forEach(function(row, i) {
+                                            if(_.has($scope, 'selectedTemplate.id') && row.id === $scope.selectedTemplate.id) {
+                                                $scope.job_templates[i].checked = 1;
+                                            }
+                                            else {
+                                                $scope.job_templates[i].checked = 0;
+                                            }
+                                        });
+                                    });
+
+                                    $scope.$watch('activeTab', () => {
+                                        if(!$scope.activeTab || $scope.activeTab !== "jobs") {
                                             $scope.job_templates.forEach(function(row, i) {
                                                 $scope.job_templates[i].checked = 0;
                                             });
                                         }
-                                        else {
-                                            if($scope.selectedTemplate){
-                                                $scope.job_templates.forEach(function(row, i) {
-                                                    if(row.id === $scope.selectedTemplate.id) {
-                                                        $scope.job_templates[i].checked = 1;
-                                                    }
-                                                    else {
-                                                        $scope.job_templates[i].checked = 0;
-                                                    }
-                                                });
-                                            }
-                                        }
                                     });
 
                                     $scope.$on('clearWorkflowLists', function() {
@@ -552,30 +550,29 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p
                                                     name: row.name
                                                 };
 
-                                                $scope.templateSelected(row);
+                                                $scope.templateManuallySelected(row);
                                             }
                                         });
 
                                     };
 
-                                    $scope.$on('templateSelected', function(e, options) {
-                                        if(options.activeTab !== 'inventory_sync') {
+                                    $scope.$watch('selectedTemplate', () => {
+                                        $scope.workflow_inventory_sources.forEach(function(row, i) {
+                                            if(_.has($scope, 'selectedTemplate.id') && row.id === $scope.selectedTemplate.id) {
+                                                $scope.workflow_inventory_sources[i].checked = 1;
+                                            }
+                                            else {
+                                                $scope.workflow_inventory_sources[i].checked = 0;
+                                            }
+                                        });
+                                    });
+
+                                    $scope.$watch('activeTab', () => {
+                                        if(!$scope.activeTab || $scope.activeTab !== "inventory_sync") {
                                             $scope.workflow_inventory_sources.forEach(function(row, i) {
                                                 $scope.workflow_inventory_sources[i].checked = 0;
                                             });
                                         }
-                                        else {
-                                            if($scope.selectedTemplate){
-                                                $scope.workflow_inventory_sources.forEach(function(row, i) {
-                                                    if(row.id === $scope.selectedTemplate.id) {
-                                                        $scope.workflow_inventory_sources[i].checked = 1;
-                                                    }
-                                                    else {
-                                                        $scope.workflow_inventory_sources[i].checked = 0;
-                                                    }
-                                                });
-                                            }
-                                        }
                                     });
 
                                     $scope.$on('clearWorkflowLists', function() {
@@ -630,30 +627,29 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p
                                                     name: row.name
                                                 };
 
-                                                $scope.templateSelected(row);
+                                                $scope.templateManuallySelected(row);
                                             }
                                         });
 
                                     };
 
-                                    $scope.$on('templateSelected', function(e, options) {
-                                        if(options.activeTab !== 'project_sync') {
+                                    $scope.$watch('selectedTemplate', () => {
+                                        $scope.projects.forEach(function(row, i) {
+                                            if(_.has($scope, 'selectedTemplate.id') && row.id === $scope.selectedTemplate.id) {
+                                                $scope.projects[i].checked = 1;
+                                            }
+                                            else {
+                                                $scope.projects[i].checked = 0;
+                                            }
+                                        });
+                                    });
+
+                                    $scope.$watch('activeTab', () => {
+                                        if(!$scope.activeTab || $scope.activeTab !== "project_sync") {
                                             $scope.projects.forEach(function(row, i) {
                                                 $scope.projects[i].checked = 0;
                                             });
                                         }
-                                        else {
-                                            if($scope.selectedTemplate){
-                                                $scope.projects.forEach(function(row, i) {
-                                                    if(row.id === $scope.selectedTemplate.id) {
-                                                        $scope.projects[i].checked = 1;
-                                                    }
-                                                    else {
-                                                        $scope.projects[i].checked = 0;
-                                                    }
-                                                });
-                                            }
-                                        }
                                     });
 
                                     $scope.$on('clearWorkflowLists', function() {
@@ -663,69 +659,6 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p
                                     });
                                 }
                             ]
-                        },
-                        'workflowForm@templates.editWorkflowJobTemplate.workflowMaker': {
-                            templateProvider: function(WorkflowMakerForm, GenerateForm) {
-                                let form = WorkflowMakerForm();
-                                let html = GenerateForm.buildHTML(form, {
-                                    mode: 'add',
-                                    related: false,
-                                    noPanel: true
-                                });
-                                return html;
-                            },
-                            controller: ['$scope', '$timeout', 'CreateSelect2',
-                                function($scope, $timeout, CreateSelect2) {
-                                    function resetPromptFields() {
-                                        $scope.credential = null;
-                                        $scope.credential_name = null;
-                                        $scope.inventory = null;
-                                        $scope.inventory_name = null;
-                                        $scope.job_type = null;
-                                        $scope.limit = null;
-                                        $scope.job_tags = null;
-                                        $scope.skip_tags = null;
-                                    }
-
-                                    $scope.saveNodeForm = function(){
-                                        // Gather up all of our form data - then let the main scope know what
-                                        // the new data is
-
-                                        $scope.confirmNodeForm({
-                                            skip_tags: $scope.skip_tags,
-                                            job_tags: $scope.job_tags,
-                                            limit: $scope.limit,
-                                            credential: $scope.credential,
-                                            credential_name: $scope.credential_name,
-                                            inventory: $scope.inventory,
-                                            inventory_name: $scope.inventory_name,
-                                            edgeType: $scope.edgeType,
-                                            job_type: $scope.job_type
-                                        });
-                                    };
-
-                                    $scope.$on('templateSelected', function(e, options) {
-
-                                        resetPromptFields();
-                                        // Loop across the preset values and attach them to scope
-                                        _.forOwn(options.presetValues, function(value, key) {
-                                            $scope[key] = value;
-                                        });
-
-                                        // The default needs to be in place before we can select2-ify the dropdown
-                                        $timeout(function() {
-                                            CreateSelect2({
-                                                element: '#workflow_maker_job_type',
-                                                multiple: false
-                                            });
-                                        });
-                                    });
-
-                                    $scope.$on('setEdgeType', function(e, edgeType) {
-                                        $scope.edgeType = edgeType;
-                                    });
-                                }
-                            ]
                         }
                     },
                     resolve: {
@@ -798,114 +731,114 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p
                     }
                 };
 
-                inventoryLookup = {
-                    searchPrefix: 'inventory',
-                    name: 'templates.editWorkflowJobTemplate.workflowMaker.inventory',
-                    url: '/inventory',
-                    data: {
-                        formChildState: true
-                    },
-                    params: {
-                        inventory_search: {
-                            value: {
-                                page_size: '5'
-                            },
-                            squash: true,
-                            dynamic: true
-                        }
-                    },
-                    ncyBreadcrumb: {
-                        skip: true
-                    },
-                    views: {
-                        'related': {
-                            templateProvider: function(ListDefinition, generateList) {
-                                let list_html = generateList.build({
-                                    mode: 'lookup',
-                                    list: ListDefinition,
-                                    input_type: 'radio'
-                                });
-                                return `<lookup-modal>${list_html}</lookup-modal>`;
-
-                            }
-                        }
-                    },
-                    resolve: {
-                        ListDefinition: ['InventoryList', function(InventoryList) {
-                            // mutate the provided list definition here
-                            let list = _.cloneDeep(InventoryList);
-                            list.lookupConfirmText = 'SELECT';
-                            return list;
-                        }],
-                        Dataset: ['ListDefinition', 'QuerySet', '$stateParams', 'GetBasePath',
-                            (list, qs, $stateParams, GetBasePath) => {
-                                let path = GetBasePath(list.name) || GetBasePath(list.basePath);
-                                return qs.search(path, $stateParams[`${list.iterator}_search`]);
-                            }
-                        ]
-                    },
-                    onExit: function($state) {
-                        if ($state.transition) {
-                            $('#form-modal').modal('hide');
-                            $('.modal-backdrop').remove();
-                            $('body').removeClass('modal-open');
-                        }
-                    },
-                };
-
-                credentialLookup = {
-                    searchPrefix: 'credential',
-                    name: 'templates.editWorkflowJobTemplate.workflowMaker.credential',
-                    url: '/credential',
-                    data: {
-                        formChildState: true
-                    },
-                    params: {
-                        credential_search: {
-                            value: {
-                                page_size: '5'
-                            },
-                            squash: true,
-                            dynamic: true
-                        }
-                    },
-                    ncyBreadcrumb: {
-                        skip: true
-                    },
-                    views: {
-                        'related': {
-                            templateProvider: function(ListDefinition, generateList) {
-                                let list_html = generateList.build({
-                                    mode: 'lookup',
-                                    list: ListDefinition,
-                                    input_type: 'radio'
-                                });
-                                return `<lookup-modal>${list_html}</lookup-modal>`;
-
-                            }
-                        }
-                    },
-                    resolve: {
-                        ListDefinition: ['CredentialList', function(CredentialList) {
-                            let list = _.cloneDeep(CredentialList);
-                            list.lookupConfirmText = 'SELECT';
-                            return list;
-                        }],
-                        Dataset: ['ListDefinition', 'QuerySet', '$stateParams', 'GetBasePath',
-                            (list, qs, $stateParams, GetBasePath) => {
-                                let path = GetBasePath(list.name) || GetBasePath(list.basePath);
-                                return qs.search(path, $stateParams[`${list.iterator}_search`]);
-                            }
-                        ]
-                    },
-                    onExit: function($state) {
-                        if ($state.transition) {
-                            $('#form-modal').modal('hide');
-                            $('.modal-backdrop').remove();
-                            $('body').removeClass('modal-open');
-                        }
-                    },
-                };
+                // inventoryLookup = {
+                //     searchPrefix: 'inventory',
+                //     name: 'templates.editWorkflowJobTemplate.workflowMaker.inventory',
+                //     url: '/inventory',
+                //     data: {
+                //         formChildState: true
+                //     },
+                //     params: {
+                //         inventory_search: {
+                //             value: {
+                //                 page_size: '5'
+                //             },
+                //             squash: true,
+                //             dynamic: true
+                //         }
+                //     },
+                //     ncyBreadcrumb: {
+                //         skip: true
+                //     },
+                //     views: {
+                //         'related': {
+                //             templateProvider: function(ListDefinition, generateList) {
+                //                 let list_html = generateList.build({
+                //                     mode: 'lookup',
+                //                     list: ListDefinition,
+                //                     input_type: 'radio'
+                //                 });
+                //                 return `<lookup-modal>${list_html}</lookup-modal>`;
+                //
+                //             }
+                //         }
+                //     },
+                //     resolve: {
+                //         ListDefinition: ['InventoryList', function(InventoryList) {
+                //             // mutate the provided list definition here
+                //             let list = _.cloneDeep(InventoryList);
+                //             list.lookupConfirmText = 'SELECT';
+                //             return list;
+                //         }],
+                //         Dataset: ['ListDefinition', 'QuerySet', '$stateParams', 'GetBasePath',
+                //             (list, qs, $stateParams, GetBasePath) => {
+                //                 let path = GetBasePath(list.name) || GetBasePath(list.basePath);
+                //                 return qs.search(path, $stateParams[`${list.iterator}_search`]);
+                //             }
+                //         ]
+                //     },
+                //     onExit: function($state) {
+                //         if ($state.transition) {
+                //             $('#form-modal').modal('hide');
+                //             $('.modal-backdrop').remove();
+                //             $('body').removeClass('modal-open');
+                //         }
+                //     },
+                // };
+                //
+                // credentialLookup = {
+                //     searchPrefix: 'credential',
+                //     name: 'templates.editWorkflowJobTemplate.workflowMaker.credential',
+                //     url: '/credential',
+                //     data: {
+                //         formChildState: true
+                //     },
+                //     params: {
+                //         credential_search: {
+                //             value: {
+                //                 page_size: '5'
+                //             },
+                //             squash: true,
+                //             dynamic: true
+                //         }
+                //     },
+                //     ncyBreadcrumb: {
+                //         skip: true
+                //     },
+                //     views: {
+                //         'related': {
+                //             templateProvider: function(ListDefinition, generateList) {
+                //                 let list_html = generateList.build({
+                //                     mode: 'lookup',
+                //                     list: ListDefinition,
+                //                     input_type: 'radio'
+                //                 });
+                //                 return `<lookup-modal>${list_html}</lookup-modal>`;
+                //
+                //             }
+                //         }
+                //     },
+                //     resolve: {
+                //         ListDefinition: ['CredentialList', function(CredentialList) {
+                //             let list = _.cloneDeep(CredentialList);
+                //             list.lookupConfirmText = 'SELECT';
+                //             return list;
+                //         }],
+                //         Dataset: ['ListDefinition', 'QuerySet', '$stateParams', 'GetBasePath',
+                //             (list, qs, $stateParams, GetBasePath) => {
+                //                 let path = GetBasePath(list.name) || GetBasePath(list.basePath);
+                //                 return qs.search(path, $stateParams[`${list.iterator}_search`]);
+                //             }
+                //         ]
+                //     },
+                //     onExit: function($state) {
+                //         if ($state.transition) {
+                //             $('#form-modal').modal('hide');
+                //             $('.modal-backdrop').remove();
+                //             $('body').removeClass('modal-open');
+                //         }
+                //     },
+                // };
 
 
                 return Promise.all([
@@ -920,8 +853,8 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p
                         }, [
                             stateExtender.buildDefinition(listRoute),
                             stateExtender.buildDefinition(workflowMaker),
-                            stateExtender.buildDefinition(inventoryLookup),
-                            stateExtender.buildDefinition(credentialLookup)
+                            // stateExtender.buildDefinition(inventoryLookup),
+                            // stateExtender.buildDefinition(credentialLookup)
                         ])
                     };
                 });
diff --git a/awx/ui/client/src/templates/prompt/prompt.partial.html b/awx/ui/client/src/templates/prompt/prompt.partial.html
index 8a99a5f7b09d..87e3f83a08b8 100644
--- a/awx/ui/client/src/templates/prompt/prompt.partial.html
+++ b/awx/ui/client/src/templates/prompt/prompt.partial.html
@@ -1,5 +1,5 @@
 <div class="Prompt">
-    <at-modal>
+    <at-modal ng-if="vm.promptData">
         <at-tab-group>
             <at-tab ng-if="vm.steps.inventory.tab" state="vm.steps.inventory.tab">{{:: vm.strings.get('prompt.INVENTORY') }}</at-tab>
             <at-tab ng-if="vm.steps.credential.tab" state="vm.steps.credential.tab">{{:: vm.strings.get('prompt.CREDENTIAL') }}</at-tab>
diff --git a/awx/ui/client/src/templates/prompt/prompt.service.js b/awx/ui/client/src/templates/prompt/prompt.service.js
index 9090bfda0595..5ab5002c2784 100644
--- a/awx/ui/client/src/templates/prompt/prompt.service.js
+++ b/awx/ui/client/src/templates/prompt/prompt.service.js
@@ -19,7 +19,7 @@ function PromptService (Empty, $filter)  {
         let skipTags = _.has(params, 'currentValues.skip_tags') && params.currentValues.skip_tags ? params.currentValues.skip_tags : (_.has(params, 'launchConf.defaults.skip_tags') ? params.launchConf.defaults.skip_tags : "");
         let jobTags = _.has(params, 'currentValues.job_tags') && params.currentValues.job_tags ? params.currentValues.job_tags : (_.has(params, 'launchConf.defaults.job_tags') ? params.launchConf.defaults.job_tags : "");
 
-        prompts.variables.value = _.has(params, 'launchConf.defaults.extra_vars') ? params.launchConf.defaults.extra_vars : "---";
+        prompts.variables.value = _.has(params, 'launchConf.defaults.extra_vars') && params.launchConf.defaults.extra_vars !== "" ? params.launchConf.defaults.extra_vars : "---";
         prompts.verbosity.choices = _.get(params, 'launchOptions.actions.POST.verbosity.choices', []).map(c => ({label: c[1], value: c[0]}));
         prompts.verbosity.value = _.has(params, 'currentValues.verbosity') && params.currentValues.verbosity ? _.find(prompts.verbosity.choices, item => item.value === params.currentValues.verbosity) : _.find(prompts.verbosity.choices, item => item.value === params.launchConf.defaults.verbosity);
         prompts.jobType.choices = _.get(params, 'launchOptions.actions.POST.job_type.choices', []).map(c => ({label: c[1], value: c[0]}));
diff --git a/awx/ui/client/src/templates/prompt/steps/inventory/prompt-inventory.directive.js b/awx/ui/client/src/templates/prompt/steps/inventory/prompt-inventory.directive.js
index 826981c18954..e3b1d24823c9 100644
--- a/awx/ui/client/src/templates/prompt/steps/inventory/prompt-inventory.directive.js
+++ b/awx/ui/client/src/templates/prompt/steps/inventory/prompt-inventory.directive.js
@@ -54,17 +54,17 @@ export default [ 'templateUrl', 'QuerySet', 'GetBasePath', 'generateList', '$com
                     $('#prompt-inventory').append($compile(html)(scope));
 
                     scope.$watch('promptData.prompts.inventory.value', () => {
-                        if(scope.promptData.prompts.inventory.value && scope.promptData.prompts.inventory.value.id) {
-                            // Loop across the inventories and see if one of them should be "checked"
-                            scope.inventories.forEach((row, i) => {
-                                if (row.id === scope.promptData.prompts.inventory.value.id) {
-                                    scope.inventories[i].checked = 1;
-                                }
-                                else {
-                                    scope.inventories[i].checked = 0;
-                                }
-                            });
-                        }
+                        scope.inventories.forEach((row, i) => {
+                            if (
+                                _.has(scope, 'promptData.prompts.inventory.value.id') &&
+                                row.id === scope.promptData.prompts.inventory.value.id
+                            ) {
+                                scope.inventories[i].checked = 1;
+                            }
+                            else {
+                                scope.inventories[i].checked = 0;
+                            }
+                        });
                     });
                 });
         }
diff --git a/awx/ui/client/src/templates/prompt/steps/other-prompts/prompt-other-prompts.controller.js b/awx/ui/client/src/templates/prompt/steps/other-prompts/prompt-other-prompts.controller.js
index 127c412d8817..96a087218dcf 100644
--- a/awx/ui/client/src/templates/prompt/steps/other-prompts/prompt-other-prompts.controller.js
+++ b/awx/ui/client/src/templates/prompt/steps/other-prompts/prompt-other-prompts.controller.js
@@ -5,7 +5,7 @@
  *************************************************/
 
 export default
-    ['ParseTypeChange', 'CreateSelect2', 'TemplatesStrings', function(ParseTypeChange, CreateSelect2, strings) {
+    ['ParseTypeChange', 'CreateSelect2', 'TemplatesStrings', '$timeout', function(ParseTypeChange, CreateSelect2, strings, $timeout) {
             const vm = this;
 
             vm.strings = strings;
@@ -30,10 +30,12 @@ export default
 
                 let codemirrorExtraVars = () => {
                     if(scope.promptData.launchConf.ask_variables_on_launch && !scope.promptData.prompts.variables.ignore) {
-                        ParseTypeChange({
-                            scope: scope,
-                            variable: 'extraVariables',
-                            field_id: 'job_launch_variables'
+                        $timeout(() => {
+                            ParseTypeChange({
+                                scope: scope,
+                                variable: 'extraVariables',
+                                field_id: 'job_launch_variables'
+                            });
                         });
                     }
                 };
diff --git a/awx/ui/client/src/templates/templates.service.js b/awx/ui/client/src/templates/templates.service.js
index cc322a0fb147..1a63cb6553bb 100644
--- a/awx/ui/client/src/templates/templates.service.js
+++ b/awx/ui/client/src/templates/templates.service.js
@@ -281,6 +281,15 @@ export default ['Rest', 'GetBasePath', '$q', 'NextPage', function(Rest, GetBaseP
               });
 
           return deferred.promise;
+      },
+      postWorkflowNodeCredential: function(params) {
+          // params.id
+          // params.data
+
+          var url = GetBasePath('workflow_job_template_nodes') + params.id + '/credentials';
+
+          Rest.setUrl(url);
+          return Rest.post(params.data);
       }
     };
 }];
diff --git a/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.controller.js b/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.controller.js
index 9bbe6e8996f7..85e03363522e 100644
--- a/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.controller.js
+++ b/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.controller.js
@@ -5,11 +5,14 @@
  *************************************************/
 
 export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
-    '$state', 'ProcessErrors', 'CreateSelect2', 'WorkflowMakerForm', '$q',
+    '$state', 'ProcessErrors', 'CreateSelect2', 'WorkflowMakerForm', '$q', 'JobTemplateModel',
+    'Empty', 'PromptService', 'Rest',
     function($scope, WorkflowService, GetBasePath, TemplatesService, $state,
-    ProcessErrors, CreateSelect2, WorkflowMakerForm, $q) {
+    ProcessErrors, CreateSelect2, WorkflowMakerForm, $q, JobTemplate,
+    Empty, PromptService, Rest) {
 
         let form = WorkflowMakerForm();
+        let promptWatcher, surveyQuestionWatcher;
 
         $scope.workflowMakerFormConfig = {
             nodeMode: "idle",
@@ -26,80 +29,39 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
         }];
 
         $scope.edgeFlags = {
-             conflict: false,
-             typeRestriction: null,
-             showTypeOptions: false
+             conflict: false
          };
 
-        $scope.editRequests = [];
-        $scope.associateRequests = [];
-        $scope.disassociateRequests = [];
+         $scope.edgeTypeOptions = [
+             {
+                 label: 'Always',
+                 value: 'always'
+             },
+             {
+                 label: 'On Success',
+                 value: 'success'
+             },
+             {
+                 label: 'On Failure',
+                 value: 'failure'
+             }
+         ];
+
+        let editRequests = [];
+        let associateRequests = [];
+        let disassociateRequests = [];
+        let credentialRequests = [];
 
         $scope.showKey = false;
         $scope.toggleKey = () => $scope.showKey = !$scope.showKey;
         $scope.keyClassList = `{ 'Key-menuIcon--active': showKey }`;
 
-        function init() {
-
-            let allNodes = [];
-            let page = 1;
-
-            let buildTreeFromNodes = function(){
-                WorkflowService.buildTree({
-                    workflowNodes: allNodes
-                }).then(function(data){
-                    $scope.treeData = data;
-
-                    // TODO: I think that the workflow chart directive (and eventually d3) is meddling with
-                    // this treeData object and removing the children object for some reason (?)
-                    // This happens on occasion and I think is a race condition (?)
-                    if(!$scope.treeData.data.children) {
-                        $scope.treeData.data.children = [];
-                    }
-
-                    $scope.treeData.workflow_job_template_obj = $scope.workflowJobTemplateObj;
-
-                    $scope.treeDataMaster = angular.copy($scope.treeData.data);
-                    $scope.showManualControls = false;
-                });
-            };
-
-            let getNodes = function(){
-                // Get the workflow nodes
-                TemplatesService.getWorkflowJobTemplateNodes($scope.workflowJobTemplateObj.id, page)
-                .then(function(data){
-                    for(var i=0; i<data.data.results.length; i++) {
-                        allNodes.push(data.data.results[i]);
-                    }
-                    if(data.data.next) {
-                        // Get the next page
-                        page++;
-                        getNodes();
-                    }
-                    else {
-                        // This is the last page
-                        buildTreeFromNodes();
-                    }
-                }, function(error){
-                    ProcessErrors($scope, error.data, error.status, form, {
-                        hdr: 'Error!',
-                        msg: 'Failed to get workflow job template nodes. GET returned ' +
-                        'status: ' + error.status
-                    });
-                });
-            };
-
-            getNodes();
-        }
-
         function resetNodeForm() {
             $scope.workflowMakerFormConfig.nodeMode = "idle";
-            $scope.edgeFlags.showTypeOptions = false;
             delete $scope.selectedTemplate;
             delete $scope.placeholderNode;
             delete $scope.betweenTwoNodes;
             $scope.nodeBeingEdited = null;
-            $scope.edgeFlags.typeRestriction = null;
             $scope.workflowMakerFormConfig.activeTab = "jobs";
 
             $scope.$broadcast('clearWorkflowLists');
@@ -118,24 +80,57 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
                 // Check to see if the user has provided any prompt values that are different
                 // from the defaults in the job template
 
-                if(params.node.unifiedJobTemplate.type === "job_template" && params.node.promptValues) {
-                    if(params.node.unifiedJobTemplate.ask_credential_on_launch) {
-                        sendableNodeData.credential = !params.node.promptValues.credential || params.node.unifiedJobTemplate.summary_fields.credential.id !== params.node.promptValues.credential.id ? params.node.promptValues.credential.id : null;
+                if(params.node.unifiedJobTemplate.type === "job_template" && params.node.promptData) {
+                    if(params.node.promptData.launchConf.survey_enabled){
+                        for (var i=0; i < params.node.promptData.surveyQuestions.length; i++){
+                            var fld = params.node.promptData.surveyQuestions[i].variable;
+                            // grab all survey questions that have answers
+                            if(params.node.promptData.surveyQuestions[i].required || (params.node.promptData.surveyQuestions[i].required === false && params.node.promptData.surveyQuestions[i].model.toString()!=="")) {
+                                if(!sendableNodeData.extra_data) {
+                                    sendableNodeData.extra_data = {};
+                                }
+                                sendableNodeData.extra_data[fld] = params.node.promptData.surveyQuestions[i].model;
+                            }
+
+                            if(params.node.promptData.surveyQuestions[i].required === false && _.isEmpty(params.node.promptData.surveyQuestions[i].model)) {
+                                switch (params.node.promptData.surveyQuestions[i].type) {
+                                    // for optional text and text-areas, submit a blank string if min length is 0
+                                    // -- this is confusing, for an explanation see:
+                                    //    http://docs.ansible.com/ansible-tower/latest/html/userguide/job_templates.html#optional-survey-questions
+                                    //
+                                    case "text":
+                                    case "textarea":
+                                    if (params.node.promptData.surveyQuestions[i].min === 0) {
+                                        sendableNodeData.extra_data[fld] = "";
+                                    }
+                                    break;
+                                }
+                            }
+                        }
                     }
-                    if(params.node.unifiedJobTemplate.ask_inventory_on_launch) {
-                        sendableNodeData.inventory = !params.node.promptValues.inventory || params.node.unifiedJobTemplate.summary_fields.inventory.id !== params.node.promptValues.inventory.id ? params.node.promptValues.inventory.id : null;
+
+                    if(_.has(params, 'node.promptData.prompts.jobType.value.value') && _.get(params, 'node.promptData.launchConf.ask_job_type_on_launch')) {
+                        sendableNodeData.job_type = params.node.promptData.prompts.jobType.templateDefault === params.node.promptData.prompts.jobType.value.value ? null : params.node.promptData.prompts.jobType.value.value;
                     }
-                    if(params.node.unifiedJobTemplate.ask_limit_on_launch) {
-                        sendableNodeData.limit =  !params.node.promptValues.limit || params.node.unifiedJobTemplate.limit !== params.node.promptValues.limit ? params.node.promptValues.limit : null;
+                    if(_.has(params, 'node.promptData.prompts.tags.value') && _.get(params, 'node.promptData.launchConf.ask_tags_on_launch')){
+                        let templateDefaultJobTags = params.node.promptData.prompts.tags.templateDefault.split(',');
+                        sendableNodeData.job_tags = (_.isEqual(templateDefaultJobTags.sort(), params.node.promptData.prompts.tags.value.map(a => a.value).sort())) ? null : params.node.promptData.prompts.tags.value.map(a => a.value).join();
                     }
-                    if(params.node.unifiedJobTemplate.ask_job_type_on_launch) {
-                        sendableNodeData.job_type =  !params.node.promptValues.job_type || params.node.unifiedJobTemplate.job_type !== params.node.promptValues.job_type ? params.node.promptValues.job_type : null;
+                    if(_.has(params, 'node.promptData.prompts.skipTags.value') && _.get(params, 'node.promptData.launchConf.ask_skip_tags_on_launch')){
+                        let templateDefaultSkipTags = params.node.promptData.prompts.skipTags.templateDefault.split(',');
+                        sendableNodeData.skip_tags = (_.isEqual(templateDefaultSkipTags.sort(), params.node.promptData.prompts.skipTags.value.map(a => a.value).sort())) ? null : params.node.promptData.prompts.skipTags.value.map(a => a.value).join();
                     }
-                    if(params.node.unifiedJobTemplate.ask_tags_on_launch) {
-                        sendableNodeData.job_tags =  !params.node.promptValues.job_tags || params.node.unifiedJobTemplate.job_tags !== params.node.promptValues.job_tags ? params.node.promptValues.job_tags : null;
+                    if(_.has(params, 'node.promptData.prompts.limit.value') && _.get(params, 'node.promptData.launchConf.ask_limit_on_launch')){
+                        sendableNodeData.limit = params.node.promptData.prompts.limit.templateDefault === params.node.promptData.prompts.limit.value ? null : params.node.promptData.prompts.limit.value;
                     }
-                    if(params.node.unifiedJobTemplate.ask_skip_tags_on_launch) {
-                        sendableNodeData.skip_tags =  !params.node.promptValues.skip_tags || params.node.unifiedJobTemplate.skip_tags !== params.node.promptValues.skip_tags ? params.node.promptValues.skip_tags : null;
+                    if(_.has(params, 'node.promptData.prompts.verbosity.value.value') && _.get(params, 'node.promptData.launchConf.ask_verbosity_on_launch')){
+                        sendableNodeData.verbosity = params.node.promptData.prompts.verbosity.templateDefault === params.node.promptData.prompts.verbosity.value.value ? null : params.node.promptData.prompts.verbosity.value.value;
+                    }
+                    if(_.has(params, 'node.promptData.prompts.inventory.value') && _.get(params, 'node.promptData.launchConf.ask_inventory_on_launch')){
+                        sendableNodeData.inventory = _.has(params, 'node.promptData.prompts.inventory.templateDefault.id') && params.node.promptData.prompts.inventory.templateDefault.id === params.node.promptData.prompts.inventory.value.id ? null : params.node.promptData.prompts.inventory.value.id;
+                    }
+                    if(_.has(params, 'node.promptData.prompts.diffMode.value') && _.get(params, 'node.promptData.launchConf.ask_diff_mode_on_launch')){
+                        sendableNodeData.diff_mode = params.node.promptData.prompts.diffMode.templateDefault === params.node.promptData.prompts.diffMode.value ? null : params.node.promptData.prompts.diffMode.value;
                     }
                 }
 
@@ -184,13 +179,33 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
                 .then(function(data) {
 
                     if(!params.node.isRoot) {
-                        $scope.associateRequests.push({
+                        associateRequests.push({
                             parentId: params.parentId,
                             nodeId: data.data.id,
                             edge: params.node.edgeType
                         });
                     }
 
+                    if(_.get(params, 'node.promptData.launchConf.ask_credential_on_launch')){
+                         // This finds the credentials that were selected in the prompt but don't occur
+                         // in the template defaults
+                         let credentialsToPost = params.node.promptData.prompts.credentials.value.filter(function(credFromPrompt) {
+                             let defaultCreds = params.node.promptData.launchConf.defaults.credentials ? params.node.promptData.launchConf.defaults.credentials : [];
+                             return !defaultCreds.some(function(defaultCred) {
+                                 return credFromPrompt.id === defaultCred.id;
+                             });
+                         });
+
+                         credentialsToPost.forEach((credentialToPost) => {
+                             credentialRequests.push({
+                                 id: data.data.id,
+                                 data: {
+                                     id: credentialToPost.id
+                                 }
+                             });
+                         });
+                     }
+
                     params.node.isNew = false;
                     continueRecursing(data.data.id);
                 }, function(error) {
@@ -207,11 +222,51 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
 
                     if(params.node.edited) {
 
-                        $scope.editRequests.push({
+                        editRequests.push({
                             id: params.node.nodeId,
                             data: buildSendableNodeData()
                         });
 
+                        if(_.get(params, 'node.promptData.launchConf.ask_credential_on_launch')){
+                             let credentialsNotInPriorCredentials = params.node.promptData.prompts.credentials.value.filter(function(credFromPrompt) {
+                                 let defaultCreds = params.node.promptData.launchConf.defaults.credentials ? params.node.promptData.launchConf.defaults.credentials : [];
+                                 return !defaultCreds.some(function(defaultCred) {
+                                     return credFromPrompt.id === defaultCred.id;
+                                 });
+                             });
+
+                             let credentialsToAdd = credentialsNotInPriorCredentials.filter(function(credNotInPrior) {
+                                 return !params.node.promptData.prompts.credentials.previousOverrides.some(function(priorCred) {
+                                     return credNotInPrior.id === priorCred.id;
+                                 });
+                             });
+
+                             let credentialsToRemove = params.node.promptData.prompts.credentials.previousOverrides.filter(function(priorCred) {
+                                 return !credentialsNotInPriorCredentials.some(function(credNotInPrior) {
+                                     return priorCred.id === credNotInPrior.id;
+                                 });
+                             });
+
+                             credentialsToAdd.forEach((credentialToAdd) => {
+                                 credentialRequests.push({
+                                     id: params.node.nodeId,
+                                     data: {
+                                         id: credentialToAdd.id
+                                     }
+                                 });
+                             });
+
+                             credentialsToRemove.forEach((credentialToRemove) => {
+                                 credentialRequests.push({
+                                     id: params.node.nodeId,
+                                     data: {
+                                         id: credentialToRemove.id,
+                                         disassociate: true
+                                     }
+                                 });
+                             });
+                         }
+
                     }
 
                     if((params.node.originalParentId && params.parentId !== params.node.originalParentId) || params.node.originalEdge !== params.node.edgeType) {//beep
@@ -225,7 +280,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
                         });
 
                         if(!parentIsDeleted) {
-                            $scope.disassociateRequests.push({
+                            disassociateRequests.push({
                                 parentId: params.node.originalParentId,
                                 nodeId: params.node.nodeId,
                                 edge: params.node.originalEdge
@@ -236,7 +291,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
                         // If we don't have a parent then this is a root node
                         // and the act of disassociating will make it a root node
                         if(params.parentId) {
-                            $scope.associateRequests.push({
+                            associateRequests.push({
                                 parentId: params.parentId,
                                 nodeId: params.node.nodeId,
                                 edge: params.node.edgeType
@@ -246,7 +301,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
                     }
                     else if(!params.node.originalParentId && params.parentId) {
                         // This used to be a root node but is now not a root node
-                        $scope.associateRequests.push({
+                        associateRequests.push({
                             parentId: params.parentId,
                             nodeId: params.node.nodeId,
                             edge: params.node.edgeType
@@ -259,12 +314,68 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
             }
         }
 
-        $scope.lookUpInventory = function(){
-            $state.go('.inventory');
+        let updateEdgeDropdownOptions = (optionsToInclude) => {
+            // Not passing optionsToInclude will include all by default
+            if(!optionsToInclude) {
+                $scope.edgeTypeOptions = [
+                    {
+                        label: 'Always',
+                        value: 'always'
+                    },
+                    {
+                        label: 'On Success',
+                        value: 'success'
+                    },
+                    {
+                        label: 'On Failure',
+                        value: 'failure'
+                    }
+                ];
+            } else {
+                $scope.edgeTypeOptions = [];
+
+                optionsToInclude.forEach((optionToInclude) => {
+                    if(optionToInclude === "always") {
+                        $scope.edgeTypeOptions.push({
+                            label: 'Always',
+                            value: 'always'
+                        });
+                    } else if(optionToInclude === "success") {
+                        $scope.edgeTypeOptions.push({
+                            label: 'On Success',
+                            value: 'success'
+                        });
+                    } else if(optionToInclude === "failure") {
+                        $scope.edgeTypeOptions.push({
+                            label: 'On Failure',
+                            value: 'failure'
+                        });
+                    }
+                });
+            }
+
+            CreateSelect2({
+                element: '#workflow_node_edge',
+                multiple: false
+            });
         };
 
-        $scope.lookUpCredential = function(){
-            $state.go('.credential');
+        let watchForPromptChanges = () => {
+            let promptDataToWatch = [
+                'promptData.prompts.inventory.value',
+                'promptData.prompts.verbosity.value',
+                'missingSurveyValue'
+            ];
+
+            promptWatcher = $scope.$watchGroup(promptDataToWatch, function() {
+                let missingPromptValue = false;
+                if($scope.missingSurveyValue) {
+                    missingPromptValue = true;
+                } else if(!$scope.promptData.prompts.inventory.value || !$scope.promptData.prompts.inventory.value.id) {
+                    missingPromptValue = true;
+                }
+                $scope.promptModalMissingReqFields = missingPromptValue;
+            });
         };
 
         $scope.closeWorkflowMaker = function() {
@@ -280,7 +391,7 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
             if($scope.treeData && $scope.treeData.data && $scope.treeData.data.children && $scope.treeData.data.children.length > 0) {
                 let completionCallback = function() {
 
-                    let disassociatePromises = $scope.disassociateRequests.map(function(request) {
+                    let disassociatePromises = disassociateRequests.map(function(request) {
                         return TemplatesService.disassociateWorkflowNode({
                             parentId: request.parentId,
                             nodeId: request.nodeId,
@@ -288,7 +399,14 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
                         });
                     });
 
-                    let editNodePromises = $scope.editRequests.map(function(request) {
+                    let credentialPromises = credentialRequests.map(function(request) {
+                        return TemplatesService.postWorkflowNodeCredential({
+                            id: request.id,
+                            data: request.data
+                        });
+                    });
+
+                    let editNodePromises = editRequests.map(function(request) {
                         return TemplatesService.editWorkflowNode({
                             id: request.id,
                             data: request.data
@@ -299,10 +417,10 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
                         return TemplatesService.deleteWorkflowJobTemplateNode(nodeId);
                     });
 
-                    $q.all(disassociatePromises.concat(editNodePromises, deletePromises))
+                    $q.all(disassociatePromises.concat(editNodePromises, deletePromises, credentialPromises))
                     .then(function() {
 
-                        let associatePromises = $scope.associateRequests.map(function(request) {
+                        let associatePromises = associateRequests.map(function(request) {
                             return TemplatesService.associateWorkflowNode({
                                 parentId: request.parentId,
                                 nodeId: request.nodeId,
@@ -364,59 +482,41 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
             });
 
             // Set the default to success
-            let edgeType = "success";
+            let edgeType = {label: "On Success", value: "success"};
 
             if (parent && ((betweenTwoNodes && parent.source.isStartNode) || (!betweenTwoNodes && parent.isStartNode))) {
                 // We don't want to give the user the option to select
                 // a type as this node will always be executed
-                edgeType = "always";
-                $scope.edgeFlags.showTypeOptions = false;
+                updateEdgeDropdownOptions(["always"]);
+                edgeType = {label: "Always", value: "always"};
             } else {
-                if ($scope.placeholderNode.edgeConflict) {
-                    // This is a conflicted scenario but we'll just let the user keep building - they will have to remediate before saving
-                    $scope.edgeFlags.typeRestriction = null;
-                } else if (_.includes(siblingConnectionTypes, "success") || _.includes(siblingConnectionTypes, "failure")) {
-                    $scope.edgeFlags.typeRestriction = "successFailure";
-                    edgeType = "success";
+                if (_.includes(siblingConnectionTypes, "success") || _.includes(siblingConnectionTypes, "failure")) {
+                    updateEdgeDropdownOptions(["success", "failure"]);
+                    edgeType = {label: "On Success", value: "success"};
                 } else if (_.includes(siblingConnectionTypes, "always")) {
-                    $scope.edgeFlags.typeRestriction = "always";
-                    edgeType = "always";
+                    updateEdgeDropdownOptions(["always"]);
+                    edgeType = {label: "Always", value: "always"};
                 } else {
-                    $scope.edgeFlags.typeRestriction = null;
+                    updateEdgeDropdownOptions();
                 }
-
-                $scope.edgeFlags.showTypeOptions = true;
             }
 
             // Reset the edgeConflict flag
             resetEdgeConflict();
 
-            $scope.$broadcast("setEdgeType", edgeType);
+            $scope.edgeType = edgeType;
             $scope.$broadcast("refreshWorkflowChart");
 
         };
 
-        $scope.confirmNodeForm = function(formValues) {
+        $scope.confirmNodeForm = function() {
             if ($scope.workflowMakerFormConfig.nodeMode === "add") {
-                if ($scope.selectedTemplate && formValues.edgeType) {
+                if ($scope.selectedTemplate && $scope.edgeType && $scope.edgeType.value) {
 
                     $scope.placeholderNode.unifiedJobTemplate = $scope.selectedTemplate;
-                    $scope.placeholderNode.edgeType = formValues.edgeType;
+                    $scope.placeholderNode.edgeType = $scope.edgeType.value;
                     if ($scope.placeholderNode.unifiedJobTemplate.type === 'job_template') {
-                        $scope.placeholderNode.promptValues = {
-                            credential: {
-                                id: formValues.credential,
-                                name: formValues.credential_name
-                            },
-                            inventory: {
-                                id: formValues.inventory,
-                                name: formValues.inventory_name
-                            },
-                            limit: formValues.limit,
-                            job_type: formValues.job_type && formValues.job_type.value ? formValues.job_type.value : null,
-                            job_tags: formValues.job_tags,
-                            skip_tags: formValues.skip_tags
-                        };
+                        $scope.placeholderNode.promptData = _.cloneDeep($scope.promptData);
                     }
                     $scope.placeholderNode.canEdit = true;
 
@@ -429,25 +529,12 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
 
                 }
             } else if ($scope.workflowMakerFormConfig.nodeMode === "edit") {
-                if ($scope.selectedTemplate && formValues.edgeType) {
+                if ($scope.selectedTemplate && $scope.edgeType && $scope.edgeType.value) {
                     $scope.nodeBeingEdited.unifiedJobTemplate = $scope.selectedTemplate;
-                    $scope.nodeBeingEdited.edgeType = formValues.edgeType;
+                    $scope.nodeBeingEdited.edgeType = $scope.edgeType.value;
 
                     if ($scope.nodeBeingEdited.unifiedJobTemplate.type === 'job_template') {
-                        $scope.nodeBeingEdited.promptValues = {
-                            credential: {
-                                id: formValues.credential,
-                                name: formValues.credential_name
-                            },
-                            inventory: {
-                                id: formValues.inventory,
-                                name: formValues.inventory_name
-                            },
-                            limit: formValues.limit,
-                            job_type: formValues.job_type && formValues.job_type.value ? formValues.job_type.value : null,
-                            job_tags: formValues.job_tags,
-                            skip_tags: formValues.skip_tags
-                        };
+                        $scope.nodeBeingEdited.promptData = _.cloneDeep($scope.promptData);
                     }
 
                     $scope.nodeBeingEdited.isActiveEdit = false;
@@ -458,6 +545,16 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
                 }
             }
 
+            if(promptWatcher) {
+                promptWatcher();
+            }
+
+            if(surveyQuestionWatcher) {
+                surveyQuestionWatcher();
+            }
+
+            $scope.promptData = null;
+
             // Reset the edgeConflict flag
             resetEdgeConflict();
 
@@ -475,6 +572,16 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
                 $scope.nodeBeingEdited.isActiveEdit = false;
             }
 
+            if(promptWatcher) {
+                promptWatcher();
+            }
+
+            if(surveyQuestionWatcher) {
+                surveyQuestionWatcher();
+            }
+
+            $scope.promptData = null;
+
             // Reset the edgeConflict flag
             resetEdgeConflict();
 
@@ -515,90 +622,123 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
 
                 let finishConfiguringEdit = function() {
 
-                    let formValues = {};
-
-                    if($scope.nodeBeingEdited.unifiedJobTemplate){
-                        // build any prompt values
-                        if ($scope.nodeBeingEdited.unifiedJobTemplate.ask_credential_on_launch) {
-                            if ($scope.nodeBeingEdited.promptValues && $scope.nodeBeingEdited.promptValues.credential) {
-                                formValues.credential_name = $scope.nodeBeingEdited.promptValues.credential.name;
-                                formValues.credential = $scope.nodeBeingEdited.promptValues.credential.id;
-                            } else if ($scope.nodeBeingEdited.unifiedJobTemplate.summary_fields.credential) {
-                                formValues.credential_name = $scope.nodeBeingEdited.unifiedJobTemplate.summary_fields.credential.name ? $scope.nodeBeingEdited.unifiedJobTemplate.summary_fields.credential.name : null;
-                                formValues.credential = $scope.nodeBeingEdited.unifiedJobTemplate.summary_fields.credential.id ? $scope.nodeBeingEdited.unifiedJobTemplate.summary_fields.credential.id : null;
-                            } else {
-                                formValues.credential_name = null;
-                                formValues.credential = null;
-                            }
-                        }
+                    let jobTemplate = new JobTemplate();
 
-                        if ($scope.nodeBeingEdited.unifiedJobTemplate.ask_inventory_on_launch) {
-                            if ($scope.nodeBeingEdited.promptValues && $scope.nodeBeingEdited.promptValues.inventory) {
-                                formValues.inventory_name = $scope.nodeBeingEdited.promptValues.inventory.name;
-                                formValues.inventory = $scope.nodeBeingEdited.promptValues.inventory.id;
-                            } else if ($scope.nodeBeingEdited.unifiedJobTemplate.summary_fields.inventory) {
-                                formValues.inventory_name = $scope.nodeBeingEdited.unifiedJobTemplate.summary_fields.inventory.name ? $scope.nodeBeingEdited.unifiedJobTemplate.summary_fields.inventory.name : null;
-                                formValues.inventory = $scope.nodeBeingEdited.unifiedJobTemplate.summary_fields.inventory.id ? $scope.nodeBeingEdited.unifiedJobTemplate.summary_fields.inventory.id : null;
-                            } else {
-                                formValues.inventory_name = null;
-                                formValues.inventory = null;
-                            }
-                        }
+                    Rest.setUrl($scope.nodeBeingEdited.originalNodeObj.related.credentials);
 
-                        if ($scope.nodeBeingEdited.unifiedJobTemplate.ask_job_type_on_launch) {
-                            if ($scope.nodeBeingEdited.promptValues && $scope.nodeBeingEdited.promptValues.job_type) {
-                                formValues.job_type = {
-                                    value: $scope.nodeBeingEdited.promptValues.job_type
-                                };
-                            } else if ($scope.nodeBeingEdited.originalNodeObj.job_type) {
-                                formValues.job_type = {
-                                    value: $scope.nodeBeingEdited.originalNodeObj.job_type
-                                };
-                            } else if ($scope.nodeBeingEdited.unifiedJobTemplate.job_type) {
-                                formValues.job_type = {
-                                    value: $scope.nodeBeingEdited.unifiedJobTemplate.job_type
-                                };
-                            } else {
-                                formValues.job_type = {
-                                    value: null
+                    if($scope.nodeBeingEdited.promptData) {
+                        $scope.promptData = _.cloneDeep($scope.nodeBeingEdited.promptData);
+                    }else if($scope.nodeBeingEdited.unifiedJobTemplate){
+                        $q.all([jobTemplate.optionsLaunch($scope.nodeBeingEdited.unifiedJobTemplate.id), jobTemplate.getLaunch($scope.nodeBeingEdited.unifiedJobTemplate.id), Rest.get()])
+                            .then((responses) => {
+                                let launchOptions = responses[0].data,
+                                    launchConf = responses[1].data,
+                                    workflowNodeCredentials = responses[2].data.results;
+
+                                let prompts = PromptService.processPromptValues({
+                                    launchConf: responses[1].data,
+                                    launchOptions: responses[0].data,
+                                    currentValues: $scope.nodeBeingEdited.originalNodeObj
+                                });
+
+                                let defaultCredsWithoutOverrides = [];
+
+                                prompts.credentials.previousOverrides = _.cloneDeep(workflowNodeCredentials);
+
+                                const credentialHasScheduleOverride = (templateDefaultCred) => {
+                                    let credentialHasOverride = false;
+                                    workflowNodeCredentials.forEach((scheduleCred) => {
+                                        if(templateDefaultCred.credential_type === scheduleCred.credential_type) {
+                                            if(
+                                                (!templateDefaultCred.vault_id && !scheduleCred.inputs.vault_id) ||
+                                                (templateDefaultCred.vault_id && scheduleCred.inputs.vault_id && templateDefaultCred.vault_id === scheduleCred.inputs.vault_id)
+                                            ) {
+                                                credentialHasOverride = true;
+                                            }
+                                        }
+                                    });
+
+                                    return credentialHasOverride;
                                 };
-                            }
 
-                        }
+                                if(_.has(launchConf, 'defaults.credentials')) {
+                                    launchConf.defaults.credentials.forEach((defaultCred) => {
+                                        if(!credentialHasScheduleOverride(defaultCred)) {
+                                            defaultCredsWithoutOverrides.push(defaultCred);
+                                        }
+                                    });
+                                }
+
+                                prompts.credentials.value = workflowNodeCredentials.concat(defaultCredsWithoutOverrides);
+
+                                if(!launchConf.survey_enabled &&
+                                    !launchConf.ask_inventory_on_launch &&
+                                    !launchConf.ask_credential_on_launch &&
+                                    !launchConf.ask_verbosity_on_launch &&
+                                    !launchConf.ask_job_type_on_launch &&
+                                    !launchConf.ask_limit_on_launch &&
+                                    !launchConf.ask_tags_on_launch &&
+                                    !launchConf.ask_skip_tags_on_launch &&
+                                    !launchConf.ask_diff_mode_on_launch &&
+                                    !launchConf.survey_enabled &&
+                                    !launchConf.credential_needed_to_start &&
+                                    !launchConf.inventory_needed_to_start &&
+                                    launchConf.passwords_needed_to_start.length === 0 &&
+                                    launchConf.variables_needed_to_start.length === 0) {
+                                        $scope.showPromptButton = false;
+                                } else {
+                                    $scope.showPromptButton = true;
+
+                                    if(launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory') && !_.has($scope, 'nodeBeingEdited.originalNodeObj.summary_fields.inventory')) {
+                                        $scope.promptModalMissingReqFields = true;
+                                    }
 
-                        if ($scope.nodeBeingEdited.unifiedJobTemplate.ask_limit_on_launch) {
-                            if ($scope.nodeBeingEdited.promptValues && typeof $scope.nodeBeingEdited.promptValues.limit === 'string') {
-                                formValues.limit = $scope.nodeBeingEdited.promptValues.limit;
-                            } else if (typeof $scope.nodeBeingEdited.originalNodeObj.limit === 'string') {
-                                formValues.limit = $scope.nodeBeingEdited.originalNodeObj.limit;
-                            } else if (typeof $scope.nodeBeingEdited.unifiedJobTemplate.limit === 'string') {
-                                formValues.limit = $scope.nodeBeingEdited.unifiedJobTemplate.limit;
-                            } else {
-                                formValues.limit = null;
-                            }
-                        }
-                        if ($scope.nodeBeingEdited.unifiedJobTemplate.ask_skip_tags_on_launch) {
-                            if ($scope.nodeBeingEdited.promptValues && typeof $scope.nodeBeingEdited.promptValues.skip_tags === 'string') {
-                                formValues.skip_tags = $scope.nodeBeingEdited.promptValues.skip_tags;
-                            } else if (typeof $scope.nodeBeingEdited.originalNodeObj.skip_tags === 'string') {
-                                formValues.skip_tags = $scope.nodeBeingEdited.originalNodeObj.skip_tags;
-                            } else if (typeof $scope.nodeBeingEdited.unifiedJobTemplate.skip_tags === 'string') {
-                                formValues.skip_tags = $scope.nodeBeingEdited.unifiedJobTemplate.skip_tags;
-                            } else {
-                                formValues.skip_tags = null;
-                            }
-                        }
-                        if ($scope.nodeBeingEdited.unifiedJobTemplate.ask_tags_on_launch) {
-                            if ($scope.nodeBeingEdited.promptValues && typeof $scope.nodeBeingEdited.promptValues.job_tags === 'string') {
-                                formValues.job_tags = $scope.nodeBeingEdited.promptValues.job_tags;
-                            } else if (typeof $scope.nodeBeingEdited.originalNodeObj.job_tags === 'string') {
-                                formValues.job_tags = $scope.nodeBeingEdited.originalNodeObj.job_tags;
-                            } else if (typeof $scope.nodeBeingEdited.unifiedJobTemplate.job_tags === 'string') {
-                                formValues.job_tags = $scope.nodeBeingEdited.unifiedJobTemplate.job_tags;
-                            } else {
-                                formValues.job_tags = null;
-                            }
-                        }
+                                    if(responses[1].data.survey_enabled) {
+                                        // go out and get the survey questions
+                                        jobTemplate.getSurveyQuestions($scope.nodeBeingEdited.unifiedJobTemplate.id)
+                                            .then((surveyQuestionRes) => {
+
+                                                let processed = PromptService.processSurveyQuestions({
+                                                    surveyQuestions: surveyQuestionRes.data.spec,
+                                                    extra_data: _.cloneDeep($scope.nodeBeingEdited.originalNodeObj.extra_data)
+                                                });
+
+                                                $scope.missingSurveyValue = processed.missingSurveyValue;
+
+                                                $scope.extraVars = (processed.extra_data === '' || _.isEmpty(processed.extra_data)) ? '---' : '---\n' + jsyaml.safeDump(processed.extra_data);
+
+                                                $scope.promptData = {
+                                                    launchConf: launchConf,
+                                                    launchOptions: launchOptions,
+                                                    prompts: prompts,
+                                                    surveyQuestions: surveyQuestionRes.data.spec,
+                                                    template: $scope.nodeBeingEdited.unifiedJobTemplate.id
+                                                };
+
+                                                surveyQuestionWatcher = $scope.$watch('promptData.surveyQuestions', () => {
+                                                    let missingSurveyValue = false;
+                                                    _.each($scope.promptData.surveyQuestions, (question) => {
+                                                        if(question.required && (Empty(question.model) || question.model === [])) {
+                                                            missingSurveyValue = true;
+                                                        }
+                                                    });
+                                                    $scope.missingSurveyValue = missingSurveyValue;
+                                                }, true);
+
+                                                watchForPromptChanges();
+                                            });
+                                    }
+                                    else {
+                                        $scope.promptData = {
+                                            launchConf: launchConf,
+                                            launchOptions: launchOptions,
+                                            prompts: prompts,
+                                            template: $scope.nodeBeingEdited.unifiedJobTemplate.id
+                                        };
+                                        watchForPromptChanges();
+                                    }
+                                }
+                        });
 
                         if ($scope.nodeBeingEdited.unifiedJobTemplate.type === "job_template") {
                             $scope.workflowMakerFormConfig.activeTab = "jobs";
@@ -641,31 +781,30 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
                          childId: nodeToEdit.id
                      });
 
-                     if (parent && parent.isStartNode) {
-                         // We don't want to give the user the option to select
-                         // a type as this node will always be executed
-                         $scope.edgeFlags.showTypeOptions = false;
-                     } else {
-                         if (nodeToEdit.edgeConflict) {
-                             // This is a conflicted scenario but we'll just let the user keep building - they will have to remediate before saving
-                             $scope.edgeFlags.typeRestriction = null;
-                         } else if (_.includes(siblingConnectionTypes, "success") || _.includes(siblingConnectionTypes, "failure")) {
-                             $scope.edgeFlags.typeRestriction = "successFailure";
-                         } else if (_.includes(siblingConnectionTypes, "always")) {
-                             $scope.edgeFlags.typeRestriction = "always";
-                         } else {
-                             $scope.edgeFlags.typeRestriction = null;
-                         }
+                     let edgeDropdownOptions = null;
 
-                         $scope.edgeFlags.showTypeOptions = true;
-                     }
-
-                    $scope.$broadcast('setEdgeType', $scope.nodeBeingEdited.edgeType);
+                     switch($scope.nodeBeingEdited.edgeType) {
+                        case "always":
+                            $scope.edgeType = {label: "Always", value: "always"};
+                            if(siblingConnectionTypes.length === 1 && _.includes(siblingConnectionTypes, "always")) {
+                                edgeDropdownOptions = ["always"];
+                            }
+                            break;
+                        case "success":
+                            $scope.edgeType = {label: "On Success", value: "success"};
+                            if(siblingConnectionTypes.length !== 0 && (!_.includes(siblingConnectionTypes, "always"))) {
+                                edgeDropdownOptions = ["success", "failure"];
+                            }
+                            break;
+                        case "failure":
+                            $scope.edgeType = {label: "On Failure", value: "failure"};
+                            if(siblingConnectionTypes.length !== 0 && (!_.includes(siblingConnectionTypes, "always"))) {
+                                edgeDropdownOptions = ["success", "failure"];
+                            }
+                            break;
+                    }
 
-                    $scope.$broadcast('templateSelected', {
-                        presetValues: formValues,
-                        activeTab: $scope.workflowMakerFormConfig.activeTab
-                    });
+                    updateEdgeDropdownOptions(edgeDropdownOptions);
 
                     $scope.$broadcast("refreshWorkflowChart");
                 };
@@ -679,47 +818,8 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
 
                     TemplatesService.getUnifiedJobTemplate($scope.nodeBeingEdited.unifiedJobTemplate.id)
                         .then(function(data) {
-
                             $scope.nodeBeingEdited.unifiedJobTemplate = _.clone(data.data.results[0]);
-
-                            let defers = [];
-                            let retrievingCredential = false;
-                            let retrievingInventory = false;
-
-                            if ($scope.nodeBeingEdited.unifiedJobTemplate.ask_credential_on_launch && $scope.nodeBeingEdited.originalNodeObj.credential) {
-                                defers.push(TemplatesService.getCredential($scope.nodeBeingEdited.originalNodeObj.credential));
-                                retrievingCredential = true;
-                            }
-
-                            if ($scope.nodeBeingEdited.unifiedJobTemplate.ask_inventory_on_launch && $scope.nodeBeingEdited.originalNodeObj.inventory) {
-                                defers.push(TemplatesService.getInventory($scope.nodeBeingEdited.originalNodeObj.inventory));
-                                retrievingInventory = true;
-                            }
-
-                            $q.all(defers)
-                                .then(function(responses) {
-                                    if (retrievingCredential) {
-                                        $scope.nodeBeingEdited.promptValues.credential = {
-                                            name: responses[0].data.name,
-                                            id: responses[0].data.id
-                                        };
-
-                                        if (retrievingInventory) {
-                                            $scope.nodeBeingEdited.promptValues.inventory = {
-                                                name: responses[1].data.name,
-                                                id: responses[1].data.id
-                                            };
-                                        }
-                                    } else if (retrievingInventory) {
-                                        $scope.nodeBeingEdited.promptValues.inventory = {
-                                            name: responses[0].data.name,
-                                            id: responses[0].data.id
-                                        };
-                                    }
-                                    finishConfiguringEdit();
-                                });
-
-
+                            finishConfiguringEdit();
                         }, function(error) {
                             ProcessErrors($scope, error.data, error.status, form, {
                                 hdr: 'Error!',
@@ -777,10 +877,10 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
                 $scope.$broadcast("refreshWorkflowChart");
 
                 if($scope.placeholderNode) {
-                    let edgeType = "success";
+                    let edgeType = {label: "On Success", value: "success"};
                     if($scope.placeholderNode.isRoot) {
-                        $scope.edgeFlags.showTypeOptions = false;
-                        edgeType = "always";
+                        updateEdgeDropdownOptions(["always"]);
+                        edgeType = {label: "Always", value: "always"};
                     }
                     else {
                         // we need to update the possible edges based on any new siblings
@@ -790,49 +890,76 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
                             childId: $scope.placeholderNode.id
                         });
 
-                        if ($scope.placeholderNode.edgeConflict) {
-                            // This is a conflicted scenario but we'll just let the user keep building - they will have to remediate before saving
-                            $scope.edgeFlags.typeRestriction = null;
-                        } else if (_.includes(siblingConnectionTypes, "success") || _.includes(siblingConnectionTypes, "failure")) {
-                            $scope.edgeFlags.typeRestriction = "successFailure";
-                        } else if (_.includes(siblingConnectionTypes, "always")) {
-                            $scope.edgeFlags.typeRestriction = "always";
-                            edgeType = "always";
+                        if (
+                            (_.includes(siblingConnectionTypes, "success") || _.includes(siblingConnectionTypes, "failure")) &&
+                            !_.includes(siblingConnectionTypes, "always")
+                        ) {
+                            updateEdgeDropdownOptions(["success", "failure"]);
+                        } else if (
+                            _.includes(siblingConnectionTypes, "always") &&
+                            !_.includes(siblingConnectionTypes, "success") &&
+                            !_.includes(siblingConnectionTypes, "failure")
+                        ) {
+                            updateEdgeDropdownOptions(["always"]);
+                            edgeType = {label: "Always", value: "always"};
                         } else {
-                            $scope.edgeFlags.typeRestriction = null;
+                            updateEdgeDropdownOptions();
                         }
 
-                        $scope.edgeFlags.showTypeOptions = true;
-
                     }
-                    $scope.$broadcast("setEdgeType", edgeType);
+                    $scope.edgeType = edgeType;
                 }
                 else if($scope.nodeBeingEdited) {
-                    if($scope.nodeBeingEdited.isRoot) {
-                        $scope.edgeFlags.showTypeOptions = false;
-                    }
-                    else {
-                        let siblingConnectionTypes = WorkflowService.getSiblingConnectionTypes({
-                            tree: $scope.treeData.data,
-                            parentId: $scope.nodeBeingEdited.parent.id,
-                            childId: $scope.nodeBeingEdited.id
-                        });
-
-                        if ($scope.nodeBeingEdited.edgeConflict) {
-                            // This is a conflicted scenario but we'll just let the user keep building - they will have to remediate before saving
-                            $scope.edgeFlags.typeRestriction = null;
-                        } else if (_.includes(siblingConnectionTypes, "success") || _.includes(siblingConnectionTypes, "failure")) {
-                            $scope.edgeFlags.typeRestriction = "successFailure";
-                        } else if (_.includes(siblingConnectionTypes, "always") && $scope.nodeBeingEdited.edgeType === "always") {
-                            $scope.edgeFlags.typeRestriction = "always";
-                        } else {
-                            $scope.edgeFlags.typeRestriction = null;
-                        }
-
-                        $scope.edgeFlags.showTypeOptions = true;
+                    let siblingConnectionTypes = WorkflowService.getSiblingConnectionTypes({
+                        tree: $scope.treeData.data,
+                        parentId: $scope.nodeBeingEdited.parent.id,
+                        childId: $scope.nodeBeingEdited.id
+                    });
 
+                    if (_.includes(siblingConnectionTypes, "success") || _.includes(siblingConnectionTypes, "failure")) {
+                        updateEdgeDropdownOptions(["success", "failure"]);
+                    } else if (_.includes(siblingConnectionTypes, "always") && $scope.nodeBeingEdited.edgeType === "always") {
+                        updateEdgeDropdownOptions(["always"]);
+                    } else {
+                        updateEdgeDropdownOptions();
                     }
-                    $scope.$broadcast("setEdgeType", $scope.nodeBeingEdited.edgeType);
+
+                    switch($scope.nodeBeingEdited.edgeType) {
+                       case "always":
+                           $scope.edgeType = {label: "Always", value: "always"};
+                           if (
+                               _.includes(siblingConnectionTypes, "always") &&
+                               !_.includes(siblingConnectionTypes, "success") &&
+                               !_.includes(siblingConnectionTypes, "failure")
+                           ) {
+                               updateEdgeDropdownOptions(["always"]);
+                           } else {
+                               updateEdgeDropdownOptions();
+                           }
+                           break;
+                       case "success":
+                           $scope.edgeType = {label: "On Success", value: "success"};
+                           if (
+                               (_.includes(siblingConnectionTypes, "success") || _.includes(siblingConnectionTypes, "failure")) &&
+                               !_.includes(siblingConnectionTypes, "always")
+                           ) {
+                               updateEdgeDropdownOptions(["success", "failure"]);
+                           } else {
+                               updateEdgeDropdownOptions();
+                           }
+                           break;
+                       case "failure":
+                           $scope.edgeType = {label: "On Failure", value: "failure"};
+                           if (
+                               (_.includes(siblingConnectionTypes, "success") || _.includes(siblingConnectionTypes, "failure")) &&
+                               !_.includes(siblingConnectionTypes, "always")
+                           ) {
+                               updateEdgeDropdownOptions(["success", "failure"]);
+                           } else {
+                               updateEdgeDropdownOptions();
+                           }
+                           break;
+                   }
                 }
 
                 $scope.treeData.data.totalNodes--;
@@ -846,63 +973,93 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
             }
         };
 
-        $scope.templateSelected = function(selectedTemplate) {
+        $scope.templateManuallySelected = function(selectedTemplate) {
 
             $scope.selectedTemplate = angular.copy(selectedTemplate);
 
-            let formValues = {};
-
-            if ($scope.selectedTemplate.ask_credential_on_launch) {
-                if ($scope.selectedTemplate.summary_fields.credential) {
-                    formValues.credential_name = $scope.selectedTemplate.summary_fields.credential.name ? $scope.selectedTemplate.summary_fields.credential.name : null;
-                    formValues.credential = $scope.selectedTemplate.summary_fields.credential.id ? $scope.selectedTemplate.summary_fields.credential.id : null;
-                } else {
-                    formValues.credential_name = null;
-                    formValues.credential = null;
-                }
-            }
-
-            if ($scope.selectedTemplate.ask_inventory_on_launch) {
-                if ($scope.selectedTemplate.summary_fields.inventory) {
-                    formValues.inventory_name = $scope.selectedTemplate.summary_fields.inventory.name ? $scope.selectedTemplate.summary_fields.inventory.name : null;
-                    formValues.inventory = $scope.selectedTemplate.summary_fields.inventory.id ? $scope.selectedTemplate.summary_fields.inventory.id : null;
-                } else {
-                    formValues.inventory_name = null;
-                    formValues.inventory = null;
-                }
-            }
-
-            if ($scope.selectedTemplate.ask_job_type_on_launch) {
-                formValues.job_type = {
-                    value: $scope.selectedTemplate.job_type ? $scope.selectedTemplate.job_type : null
-                };
+            if(selectedTemplate.type === "job_template") {
+                let jobTemplate = new JobTemplate();
+
+                $q.all([jobTemplate.optionsLaunch(selectedTemplate.id), jobTemplate.getLaunch(selectedTemplate.id)])
+                    .then((responses) => {
+                        let launchConf = responses[1].data;
+
+                        if(!launchConf.survey_enabled &&
+                            !launchConf.ask_inventory_on_launch &&
+                            !launchConf.ask_credential_on_launch &&
+                            !launchConf.ask_verbosity_on_launch &&
+                            !launchConf.ask_job_type_on_launch &&
+                            !launchConf.ask_limit_on_launch &&
+                            !launchConf.ask_tags_on_launch &&
+                            !launchConf.ask_skip_tags_on_launch &&
+                            !launchConf.ask_diff_mode_on_launch &&
+                            !launchConf.survey_enabled &&
+                            !launchConf.credential_needed_to_start &&
+                            !launchConf.inventory_needed_to_start &&
+                            launchConf.passwords_needed_to_start.length === 0 &&
+                            launchConf.variables_needed_to_start.length === 0) {
+                                $scope.showPromptButton = false;
+                        } else {
+                            $scope.showPromptButton = true;
 
-                // The default needs to be in place before we can select2-ify the dropdown
-                CreateSelect2({
-                    element: '#workflow_maker_job_type',
-                    multiple: false
-                });
-            }
+                            if(launchConf.ask_inventory_on_launch && !_.has(launchConf, 'defaults.inventory')) {
+                                $scope.promptModalMissingReqFields = true;
+                            }
 
-            if ($scope.selectedTemplate.ask_limit_on_launch) {
-                formValues.limit = $scope.selectedTemplate.limit ? $scope.selectedTemplate.limit : null;
-            }
+                            if(launchConf.survey_enabled) {
+                                // go out and get the survey questions
+                                jobTemplate.getSurveyQuestions(selectedTemplate.id)
+                                    .then((surveyQuestionRes) => {
+
+                                        let processed = PromptService.processSurveyQuestions({
+                                            surveyQuestions: surveyQuestionRes.data.spec
+                                        });
+
+                                        $scope.missingSurveyValue = processed.missingSurveyValue;
+
+                                        $scope.promptData = {
+                                            launchConf: responses[1].data,
+                                            launchOptions: responses[0].data,
+                                            surveyQuestions: processed.surveyQuestions,
+                                            template: selectedTemplate.id,
+                                            prompts: PromptService.processPromptValues({
+                                                launchConf: responses[1].data,
+                                                launchOptions: responses[0].data
+                                            }),
+                                        };
 
-            if ($scope.selectedTemplate.ask_skip_tags_on_launch) {
-                formValues.skip_tags = $scope.selectedTemplate.skip_tags ? $scope.selectedTemplate.skip_tags : null;
-            }
+                                        $scope.$watch('promptData.surveyQuestions', () => {
+                                            let missingSurveyValue = false;
+                                            _.each($scope.promptData.surveyQuestions, (question) => {
+                                                if(question.required && (Empty(question.model) || question.model === [])) {
+                                                    missingSurveyValue = true;
+                                                }
+                                            });
+                                            $scope.missingSurveyValue = missingSurveyValue;
+                                        }, true);
+
+                                        watchForPromptChanges();
+                                    });
+                            }
+                            else {
+                                $scope.promptData = {
+                                    launchConf: responses[1].data,
+                                    launchOptions: responses[0].data,
+                                    template: selectedTemplate.id,
+                                    prompts: PromptService.processPromptValues({
+                                        launchConf: responses[1].data,
+                                        launchOptions: responses[0].data
+                                    }),
+                                };
 
-            if ($scope.selectedTemplate.ask_tags_on_launch) {
-                formValues.job_tags = $scope.selectedTemplate.job_tags ? $scope.selectedTemplate.job_tags : null;
+                                watchForPromptChanges();
+                            }
+                        }
+                    });
+            } else {
+                // TODO - clear out prompt data?
+                $scope.showPromptButton = false;
             }
-
-            // Communicate down the scope chain to our children that a template has been selected.  This
-            // will handle populating the form properly as well as clearing out any previously selected
-            // templates in different lists
-            $scope.$broadcast('templateSelected', {
-                presetValues: formValues,
-                activeTab: $scope.workflowMakerFormConfig.activeTab
-            });
         };
 
         function resetEdgeConflict(){
@@ -944,7 +1101,61 @@ export default ['$scope', 'WorkflowService', 'GetBasePath', 'TemplatesService',
             $scope.$broadcast('zoomToFitChart');
         };
 
-        init();
+        $scope.openPromptModal = function() {
+            $scope.promptData.triggerModalOpen = true;
+        };
+
+        let allNodes = [];
+        let page = 1;
+
+        let buildTreeFromNodes = function(){
+            WorkflowService.buildTree({
+                workflowNodes: allNodes
+            }).then(function(data){
+                $scope.treeData = data;
+
+                // TODO: I think that the workflow chart directive (and eventually d3) is meddling with
+                // this treeData object and removing the children object for some reason (?)
+                // This happens on occasion and I think is a race condition (?)
+                if(!$scope.treeData.data.children) {
+                    $scope.treeData.data.children = [];
+                }
+
+                $scope.treeData.workflow_job_template_obj = $scope.workflowJobTemplateObj;
+
+                $scope.treeDataMaster = angular.copy($scope.treeData.data);
+                $scope.showManualControls = false;
+            });
+        };
+
+        let getNodes = function(){
+            // Get the workflow nodes
+            TemplatesService.getWorkflowJobTemplateNodes($scope.workflowJobTemplateObj.id, page)
+            .then(function(data){
+                for(var i=0; i<data.data.results.length; i++) {
+                    allNodes.push(data.data.results[i]);
+                }
+                if(data.data.next) {
+                    // Get the next page
+                    page++;
+                    getNodes();
+                }
+                else {
+                    // This is the last page
+                    buildTreeFromNodes();
+                }
+            }, function(error){
+                ProcessErrors($scope, error.data, error.status, form, {
+                    hdr: 'Error!',
+                    msg: 'Failed to get workflow job template nodes. GET returned ' +
+                    'status: ' + error.status
+                });
+            });
+        };
+
+        getNodes();
+
+        updateEdgeDropdownOptions();
 
     }
 ];
diff --git a/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.partial.html b/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.partial.html
index 63986f9e8165..5d5db54ca1d8 100644
--- a/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.partial.html
+++ b/awx/ui/client/src/templates/workflows/workflow-maker/workflow-maker.partial.html
@@ -93,7 +93,31 @@
                     <div id="workflow-project-sync-list" ng-show="workflowMakerFormConfig.activeTab === 'project_sync'" ui-view="projectSyncList"></div>
                     <div id="workflow-inventory-sync-list" ng-show="workflowMakerFormConfig.activeTab === 'inventory_sync'" ui-view="inventorySyncList"></div>
                 </div>
-                <div id="workflow-maker-form" ui-view="workflowForm"></div>
+                <div ng-show="selectedTemplate">
+                    <div class="form-group Form-formGroup Form-formGroup--singleColumn">
+                        <label for="verbosity" class="Form-inputLabelContainer">
+                            <span class="Form-requiredAsterisk">*</span>
+                            <span class="Form-inputLabel">RUN</span>
+                        </label>
+                        <div>
+                            <select
+                                id="workflow_node_edge"
+                                ng-options="v as v.label for v in edgeTypeOptions track by v.value"
+                                ng-model="edgeType"
+                                class="form-control Form-dropDown"
+                                name="edgeType"
+                                tabindex="-1"
+                                aria-hidden="true">
+                            </select>
+                        </div>
+                    </div>
+                    <div class="buttons Form-buttons" id="workflow_maker_controls">
+                        <button type="button" class="btn btn-sm Form-primaryButton Form-primaryButton--noMargin" id="workflow_maker_prompt_btn" ng-show="showPromptButton" ng-click="openPromptModal()"> Prompt</button>
+                        <button type="button" class="btn btn-sm Form-cancelButton" id="workflow_maker_cancel_btn" ng-show="(workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)" ng-click="cancelNodeForm()"> Cancel</button>
+                        <button type="button" class="btn btn-sm Form-cancelButton" id="workflow_maker_close_btn" ng-show="!(workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)" ng-click="cancelNodeForm()"> Close</button>
+                        <button type="button" class="btn btn-sm Form-saveButton" id="workflow_maker_select_btn" ng-show="(workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)" ng-click="confirmNodeForm()" ng-disabled="workflow_maker_form.$invalid || !selectedTemplate || promptModalMissingReqFields" disabled="disabled"> Select</button>
+                    </div>
+                </div>
             </div>
         </div>
     </div>
@@ -101,4 +125,5 @@
         <button type="button" class="btn btn-sm WorkflowMaker-cancelButton" ng-click="closeWorkflowMaker()"> Close</button>
         <button type="button" class="btn btn-sm WorkflowMaker-saveButton" ng-click="saveWorkflowMaker()" ng-show="workflowJobTemplateObj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate" ng-disabled="edgeFlags.conflict || workflowMakerFormConfig.nodeMode === 'add'"> Save</button>
     </div>
+    <prompt prompt-data="promptData" action-text="CONFIRM"></launch>
 </div>


Code review comments:
diff_hunk: @@ -798,114 +731,114 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p
                     }
                 };
 
-                inventoryLookup = {
-                    searchPrefix: 'inventory',
-                    name: 'templates.editWorkflowJobTemplate.workflowMaker.inventory',
-                    url: '/inventory',
-                    data: {
-                        formChildState: true
-                    },
-                    params: {
-                        inventory_search: {
-                            value: {
-                                page_size: '5'
-                            },
-                            squash: true,
-                            dynamic: true
-                        }
-                    },
-                    ncyBreadcrumb: {
-                        skip: true
-                    },
-                    views: {
-                        'related': {
-                            templateProvider: function(ListDefinition, generateList) {
-                                let list_html = generateList.build({
-                                    mode: 'lookup',
-                                    list: ListDefinition,
-                                    input_type: 'radio'
-                                });
-                                return `<lookup-modal>${list_html}</lookup-modal>`;
-
-                            }
-                        }
-                    },
-                    resolve: {
-                        ListDefinition: ['InventoryList', function(InventoryList) {
-                            // mutate the provided list definition here
-                            let list = _.cloneDeep(InventoryList);
-                            list.lookupConfirmText = 'SELECT';
-                            return list;
-                        }],
-                        Dataset: ['ListDefinition', 'QuerySet', '$stateParams', 'GetBasePath',
-                            (list, qs, $stateParams, GetBasePath) => {
-                                let path = GetBasePath(list.name) || GetBasePath(list.basePath);
-                                return qs.search(path, $stateParams[`${list.iterator}_search`]);
-                            }
-                        ]
-                    },
-                    onExit: function($state) {
-                        if ($state.transition) {
-                            $('#form-modal').modal('hide');
-                            $('.modal-backdrop').remove();
-                            $('body').removeClass('modal-open');
-                        }
-                    },
-                };
-
-                credentialLookup = {
-                    searchPrefix: 'credential',
-                    name: 'templates.editWorkflowJobTemplate.workflowMaker.credential',
-                    url: '/credential',
-                    data: {
-                        formChildState: true
-                    },
-                    params: {
-                        credential_search: {
-                            value: {
-                                page_size: '5'
-                            },
-                            squash: true,
-                            dynamic: true
-                        }
-                    },
-                    ncyBreadcrumb: {
-                        skip: true
-                    },
-                    views: {
-                        'related': {
-                            templateProvider: function(ListDefinition, generateList) {
-                                let list_html = generateList.build({
-                                    mode: 'lookup',
-                                    list: ListDefinition,
-                                    input_type: 'radio'
-                                });
-                                return `<lookup-modal>${list_html}</lookup-modal>`;
-
-                            }
-                        }
-                    },
-                    resolve: {
-                        ListDefinition: ['CredentialList', function(CredentialList) {
-                            let list = _.cloneDeep(CredentialList);
-                            list.lookupConfirmText = 'SELECT';
-                            return list;
-                        }],
-                        Dataset: ['ListDefinition', 'QuerySet', '$stateParams', 'GetBasePath',
-                            (list, qs, $stateParams, GetBasePath) => {
-                                let path = GetBasePath(list.name) || GetBasePath(list.basePath);
-                                return qs.search(path, $stateParams[`${list.iterator}_search`]);
-                            }
-                        ]
-                    },
-                    onExit: function($state) {
-                        if ($state.transition) {
-                            $('#form-modal').modal('hide');
-                            $('.modal-backdrop').remove();
-                            $('body').removeClass('modal-open');
-                        }
-                    },
-                };
+                // inventoryLookup = {
+                //     searchPrefix: 'inventory',
+                //     name: 'templates.editWorkflowJobTemplate.workflowMaker.inventory',
+                //     url: '/inventory',
+                //     data: {
+                //         formChildState: true
+                //     },
+                //     params: {
+                //         inventory_search: {
+                //             value: {
+                //                 page_size: '5'
+                //             },
+                //             squash: true,
+                //             dynamic: true
+                //         }
+                //     },
+                //     ncyBreadcrumb: {
+                //         skip: true
+                //     },
+                //     views: {
+                //         'related': {
+                //             templateProvider: function(ListDefinition, generateList) {
+                //                 let list_html = generateList.build({
+                //                     mode: 'lookup',
+                //                     list: ListDefinition,
+                //                     input_type: 'radio'
+                //                 });
+                //                 return `<lookup-modal>${list_html}</lookup-modal>`;
+                //
+                //             }
+                //         }
+                //     },
+                //     resolve: {
+                //         ListDefinition: ['InventoryList', function(InventoryList) {
+                //             // mutate the provided list definition here
+                //             let list = _.cloneDeep(InventoryList);
+                //             list.lookupConfirmText = 'SELECT';
+                //             return list;
+                //         }],
+                //         Dataset: ['ListDefinition', 'QuerySet', '$stateParams', 'GetBasePath',
+                //             (list, qs, $stateParams, GetBasePath) => {
+                //                 let path = GetBasePath(list.name) || GetBasePath(list.basePath);
+                //                 return qs.search(path, $stateParams[`${list.iterator}_search`]);
+                //             }
+                //         ]
+                //     },
+                //     onExit: function($state) {
+                //         if ($state.transition) {
+                //             $('#form-modal').modal('hide');
+                //             $('.modal-backdrop').remove();
+                //             $('body').removeClass('modal-open');
+                //         }
+                //     },
+                // };
+                //
+                // credentialLookup = {
+                //     searchPrefix: 'credential',
+                //     name: 'templates.editWorkflowJobTemplate.workflowMaker.credential',
+                //     url: '/credential',
+                //     data: {
+                //         formChildState: true
+                //     },
+                //     params: {
+                //         credential_search: {
+                //             value: {
+                //                 page_size: '5'
+                //             },
+                //             squash: true,
+                //             dynamic: true
+                //         }
+                //     },
+                //     ncyBreadcrumb: {
+                //         skip: true
+                //     },
+                //     views: {
+                //         'related': {
+                //             templateProvider: function(ListDefinition, generateList) {
+                //                 let list_html = generateList.build({
+                //                     mode: 'lookup',
+                //                     list: ListDefinition,
+                //                     input_type: 'radio'
+                //                 });
+                //                 return `<lookup-modal>${list_html}</lookup-modal>`;
+                //
+                //             }
+                //         }
+                //     },
+                //     resolve: {
+                //         ListDefinition: ['CredentialList', function(CredentialList) {
+                //             let list = _.cloneDeep(CredentialList);
+                //             list.lookupConfirmText = 'SELECT';
+                //             return list;
+                //         }],
+                //         Dataset: ['ListDefinition', 'QuerySet', '$stateParams', 'GetBasePath',
+                //             (list, qs, $stateParams, GetBasePath) => {
+                //                 let path = GetBasePath(list.name) || GetBasePath(list.basePath);
+                //                 return qs.search(path, $stateParams[`${list.iterator}_search`]);
+                //             }
+                //         ]
+                //     },
+                //     onExit: function($state) {
+                //         if ($state.transition) {
+                //             $('#form-modal').modal('hide');
+                //             $('.modal-backdrop').remove();
+                //             $('body').removeClass('modal-open');
+                //         }
+                //     },
+                // };
path: awx/ui/client/src/templates/main.js
text: @user1:
Can this block of commented-out code be removed?

diff_hunk: @@ -920,8 +853,8 @@ angular.module('templates', [surveyMaker.name, jobTemplates.name, labels.name, p
                         }, [
                             stateExtender.buildDefinition(listRoute),
                             stateExtender.buildDefinition(workflowMaker),
-                            stateExtender.buildDefinition(inventoryLookup),
-                            stateExtender.buildDefinition(credentialLookup)
+                            // stateExtender.buildDefinition(inventoryLookup),
+                            // stateExtender.buildDefinition(credentialLookup)
path: awx/ui/client/src/templates/main.js
text: @user1:
are these still needed?