From 049a413f7aba1183fb7650236f51185e64a08c22 Mon Sep 17 00:00:00 2001 From: Chris Houseknecht Date: Tue, 28 Jan 2014 13:57:37 -0500 Subject: [PATCH] AC-941 don't display UI form fields until API data loads. Also fixed related set pagination issue. --- awx/ui/static/js/controllers/JobTemplates.js | 121 ++++++++++-------- awx/ui/static/js/controllers/Jobs.js | 50 ++++---- awx/ui/static/js/controllers/Teams.js | 2 +- awx/ui/static/js/forms/JobTemplates.js | 2 +- awx/ui/static/js/helpers/PaginationHelpers.js | 6 +- 5 files changed, 97 insertions(+), 84 deletions(-) diff --git a/awx/ui/static/js/controllers/JobTemplates.js b/awx/ui/static/js/controllers/JobTemplates.js index fe10b9cdf6..0fb999e30c 100644 --- a/awx/ui/static/js/controllers/JobTemplates.js +++ b/awx/ui/static/js/controllers/JobTemplates.js @@ -180,7 +180,6 @@ function JobTemplatesAdd ($scope, $rootScope, $compile, $location, $log, $routeP Wait('stop'); }) .error( function(data, status, headers, config) { - Wait('stop'); ProcessErrors(scope, data, status, form, { hdr: 'Error!', msg: 'Failed to get playbook list for ' + url +'. GET returned status: ' + status }); }); @@ -323,6 +322,7 @@ function JobTemplatesEdit ($scope, $rootScope, $compile, $location, $log, $route var generator = GenerateForm; var form = JobTemplateForm; var scope = generator.inject(form, {mode: 'edit', related: true}); + var loadingFinishedCount = 0; scope.parseType = 'yaml'; ParseTypeChange(scope); @@ -354,12 +354,14 @@ function JobTemplatesEdit ($scope, $rootScope, $compile, $location, $log, $route for (var i=0; i < data.length; i++) { scope.playbook_options.push(data[i]); if (data[i] == scope.playbook) { - scope['job_templates_form']['playbook'].$setValidity('required',true); + scope['job_templates_form']['playbook'].$setValidity('required',true); } } - Wait('stop'); - if (!scope.$$phase) { - scope.$digest(); + if (scope.playbook) { + scope.$emit('jobTemplateLoadFinished'); + } + else { + Wait('stop'); } }) .error( function(data, status, headers, config) { @@ -368,14 +370,12 @@ function JobTemplatesEdit ($scope, $rootScope, $compile, $location, $log, $route ' project or make the playbooks available on the file system.', 'alert-info'); }); } - else { - Wait('stop'); - } } // Detect and alert user to potential SCM status issues var checkSCMStatus = function() { - if (!Empty(scope.project)) { + if (!Empty(scope.project)) { + Wait('start'); Rest.setUrl(GetBasePath('projects') + scope.project + '/'); Rest.get() .success( function(data, status, headers, config) { @@ -395,7 +395,7 @@ function JobTemplatesEdit ($scope, $rootScope, $compile, $location, $log, $route ' the directory exists and file permissions are set correctly.'; break; } - + Wait('stop'); if (msg) { Alert('Waning', msg, 'alert-info'); } @@ -409,10 +409,10 @@ function JobTemplatesEdit ($scope, $rootScope, $compile, $location, $log, $route // Register a watcher on project_name. Refresh the playbook list on change. - if (scope.selectPlaybookUnregister) { - scope.selectPlaybookUnregister(); + if (scope.watchProjectUnregister) { + scope.watchProjectUnregister(); } - scope.selectPlaybookUnregister = scope.$watch('project_name', function(oldValue, newValue) { + scope.watchProjectUnregister = scope.$watch('project_name', function(oldValue, newValue) { if (oldValue !== newValue && newValue !== '' && newValue !== null && newValue !== undefined) { scope.playbook = null; getPlaybooks(scope.project); @@ -420,6 +420,22 @@ function JobTemplatesEdit ($scope, $rootScope, $compile, $location, $log, $route } }); + // Turn off 'Wait' after both cloud credential and playbook list come back + if (scope.removeJobTemplateLoadFinished) { + scope.removeJobTemplateLoadFinished(); + } + scope.removeJobTemplateLoadFinished = scope.$on('jobTemplateLoadFinished', function() { + loadingFinishedCount++; + if (loadingFinishedCount >= 2) { + // The initial template load finished. Now load related jobs, which + // will turn off the 'working' spinner. + for (var set in relatedSets) { + scope.search(relatedSets[set].iterator); + } + + } + }); + // Set the status/badge for each related job if (scope.removeRelatedJobs) { scope.removeRelatedJobs(); @@ -460,19 +476,18 @@ function JobTemplatesEdit ($scope, $rootScope, $compile, $location, $log, $route field: 'cloud_credential', hdr: 'Select Cloud Credential' }); + scope.$emit('jobTemplateLoadFinished'); }); + // Retrieve each related set and populate the playbook list if (scope.jobTemplateLoadedRemove) { scope.jobTemplateLoadedRemove(); } scope.jobTemplateLoadedRemove = scope.$on('jobTemplateLoaded', function(e, related_cloud_credential) { - for (var set in relatedSets) { - scope.search(relatedSets[set].iterator); - } - getPlaybooks(scope.project); - //$('#forks-slider').slider('value',scope.forks); // align slider handle with value. + getPlaybooks(scope.project); + var dft = (scope['host_config_key'] === "" || scope['host_config_key'] === null) ? 'false' : 'true'; md5Setup({ scope: scope, @@ -488,7 +503,6 @@ function JobTemplatesEdit ($scope, $rootScope, $compile, $location, $log, $route scope.$emit('cloudCredentialReady', data.name); }) .error( function(data, status, headers, config) { - Wait('stop'); ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Failed to related cloud credential. GET returned status: ' + status }); }); @@ -506,48 +520,49 @@ function JobTemplatesEdit ($scope, $rootScope, $compile, $location, $log, $route .success( function(data, status, headers, config) { LoadBreadCrumbs({ path: '/job_templates/' + id, title: data.name }); for (var fld in form.fields) { - if (fld != 'variables' && data[fld] !== null && data[fld] !== undefined) { - if (form.fields[fld].type == 'select') { - if (scope[fld + '_options'] && scope[fld + '_options'].length > 0) { - for (var i=0; i < scope[fld + '_options'].length; i++) { - if (data[fld] == scope[fld + '_options'][i].value) { - scope[fld] = scope[fld + '_options'][i]; + if (fld != 'variables' && data[fld] !== null && data[fld] !== undefined) { + if (form.fields[fld].type == 'select') { + if (scope[fld + '_options'] && scope[fld + '_options'].length > 0) { + for (var i=0; i < scope[fld + '_options'].length; i++) { + if (data[fld] == scope[fld + '_options'][i].value) { + scope[fld] = scope[fld + '_options'][i]; + } } } - } - else { + else { + scope[fld] = data[fld]; + } + } + else { scope[fld] = data[fld]; - } - } - else { - scope[fld] = data[fld]; - } - master[fld] = scope[fld]; - } - if (fld == 'variables') { - // Parse extra_vars, converting to YAML. - if ($.isEmptyObject(data.extra_vars) || data.extra_vars == "\{\}" || data.extra_vars == "null" - || data.extra_vars == "" || data.extra_vars == null) { - scope.variables = "---"; - } - else { - var json_obj = JSON.parse(data.extra_vars); - scope.variables = jsyaml.safeDump(json_obj); - } - master.variables = scope.variables; - } - if (form.fields[fld].type == 'lookup' && data.summary_fields[form.fields[fld].sourceModel]) { - scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] = + } + master[fld] = scope[fld]; + } + if (fld == 'variables') { + // Parse extra_vars, converting to YAML. + if ($.isEmptyObject(data.extra_vars) || data.extra_vars == "\{\}" || data.extra_vars == "null" + || data.extra_vars == "" || data.extra_vars == null) { + scope.variables = "---"; + } + else { + var json_obj = JSON.parse(data.extra_vars); + scope.variables = jsyaml.safeDump(json_obj); + } + master.variables = scope.variables; + } + if (form.fields[fld].type == 'lookup' && data.summary_fields[form.fields[fld].sourceModel]) { + scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] = data.summary_fields[form.fields[fld].sourceModel][form.fields[fld].sourceField]; - master[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] = + master[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField] = scope[form.fields[fld].sourceModel + '_' + form.fields[fld].sourceField]; - } + } } + scope.url = data.url; var related = data.related; for (var set in form.related) { if (related[set]) { - relatedSets[set] = { url: related[set], iterator: form.related[set].iterator }; + relatedSets[set] = { url: related[set], iterator: form.related[set].iterator }; } } @@ -586,9 +601,8 @@ function JobTemplatesEdit ($scope, $rootScope, $compile, $location, $log, $route scope.$emit('jobTemplateLoaded', data.related.cloud_credential); }) .error( function(data, status, headers, config) { - Wait('stop'); ProcessErrors(scope, data, status, form, - { hdr: 'Error!', msg: 'Failed to retrieve job template: ' + $routeParams.id + '. GET status: ' + status }); + { hdr: 'Error!', msg: 'Failed to retrieve job template: ' + $routeParams.id + '. GET status: ' + status }); }); // Save changes to the parent @@ -634,7 +648,6 @@ function JobTemplatesEdit ($scope, $rootScope, $compile, $location, $log, $route (base == 'job_templates') ? ReturnToCaller() : ReturnToCaller(1); }) .error( function(data, status, headers, config) { - Wait('stop'); ProcessErrors(scope, data, status, form, { hdr: 'Error!', msg: 'Failed to update job template. PUT returned status: ' + status }); }); diff --git a/awx/ui/static/js/controllers/Jobs.js b/awx/ui/static/js/controllers/Jobs.js index 1c806bb825..c31b1b635a 100644 --- a/awx/ui/static/js/controllers/Jobs.js +++ b/awx/ui/static/js/controllers/Jobs.js @@ -198,6 +198,7 @@ function JobsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, var master = {}; var id = $routeParams.id; var relatedSets = {}; + var loadingFinishedCount = 0; scope.job_id = id; scope.parseType = 'yaml'; @@ -213,44 +214,28 @@ function JobsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, for (var i=0; i < data.length; i++) { scope.playbook_options.push(data[i]); } + scope.$emit('jobTemplateLoadFinished'); }) .error( function(data, status, headers, config) { - //ProcessErrors(scope, data, status, form, - // { hdr: 'Error!', msg: 'Failed to get playbook list for ' + url +'. GET returned status: ' + status }); - - // Ignore the error. We get this error when the project or playbook has been deleted - + scope.$emit('jobTemplateLoadFinished'); }); } + else { + scope.$emit('jobTemplateLoadFinished'); + } } - // Register a watcher on project_name. Refresh the playbook list on change. - if (scope.selectPlaybookUnregister) { - scope.selectPlaybookUnregister(); - } - scope.selectPlaybookUnregister = scope.$watch('project_name', function(oldValue, newValue) { - if (oldValue !== newValue && newValue !== '' && newValue !== null && newValue !== undefined) { - scope.playbook = null; - getPlaybooks(scope.project); - } - }); - // Retrieve each related set and populate the playbook list if (scope.jobLoadedRemove) { scope.jobLoadedRemove(); } scope.jobLoadedRemove = scope.$on('jobLoaded', function(e, related_cloud_credential) { - scope[form.name + 'ReadOnly'] = (scope.status == 'new') ? false : true; - - // Load related sets - for (var set in relatedSets) { - scope.search(relatedSets[set].iterator); - } - // Set the playbook lookup getPlaybooks(scope.project); + scope[form.name + 'ReadOnly'] = (scope.status == 'new') ? false : true; + $('#forks-slider').slider("option", "value", scope.forks); $('#forks-slider').slider("disable"); $('input[type="checkbox"]').attr('disabled','disabled'); @@ -271,6 +256,7 @@ function JobsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, default_val: dft }); scope['callback_url'] = data.related['callback']; + scope.$emit('jobTemplateLoadFinished'); }) .error( function(data, status, headers, config) { scope['callback_url'] = '<< Job template not found >>'; @@ -282,15 +268,29 @@ function JobsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, Rest.get() .success( function(data, status, headers, config) { scope['cloud_credential_name'] = data.name; + scope.$emit('jobTemplateLoadFinished'); }) .error( function(data, status, headers, config) { ProcessErrors(scope, data, status, null, { hdr: 'Error!', msg: 'Failed to related cloud credential. GET returned status: ' + status }); }); } - - Wait('stop'); + else { + scope.$emit('jobTemplateLoadFinished'); + } + }); + // Turn off 'Wait' after both cloud credential and playbook list come back + if (scope.removeJobTemplateLoadFinished) { + scope.removeJobTemplateLoadFinished(); + } + scope.removeJobTemplateLoadFinished = scope.$on('jobTemplateLoadFinished', function() { + loadingFinishedCount++; + if (loadingFinishedCount >= 3) { + // The initial template load finished. Now load related jobs, which + // will turn off the 'working' spinner. + Wait('stop'); + } }); // Our job type options diff --git a/awx/ui/static/js/controllers/Teams.js b/awx/ui/static/js/controllers/Teams.js index 261a167f64..bc85bf9eb5 100644 --- a/awx/ui/static/js/controllers/Teams.js +++ b/awx/ui/static/js/controllers/Teams.js @@ -200,7 +200,7 @@ function TeamsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, }); // Retrieve detail record and prepopulate the form - Wait('stop'); + Wait('start'); Rest.setUrl(defaultUrl + ':id/'); Rest.get({ params: {id: id} }) .success( function(data, status, headers, config) { diff --git a/awx/ui/static/js/forms/JobTemplates.js b/awx/ui/static/js/forms/JobTemplates.js index 274a4a0d13..bfb87cf176 100644 --- a/awx/ui/static/js/forms/JobTemplates.js +++ b/awx/ui/static/js/forms/JobTemplates.js @@ -325,7 +325,7 @@ angular.module('JobTemplateFormDefinition', []) { name: "error", value: "error" }, { name: "failed", value: "failed" }, { name: "canceled", value: "canceled" } ], - badgeIcon: 'icon-job-\{\{ job.status \}\}', + badgeIcon: 'fa icon-job-\{\{ job.status \}\}', badgePlacement: 'left', badgeToolTip: "\{\{ job.statusBadgeToolTip \}\}", badgeTipPlacement: 'top', diff --git a/awx/ui/static/js/helpers/PaginationHelpers.js b/awx/ui/static/js/helpers/PaginationHelpers.js index bcab66394c..d194dd74ca 100644 --- a/awx/ui/static/js/helpers/PaginationHelpers.js +++ b/awx/ui/static/js/helpers/PaginationHelpers.js @@ -52,8 +52,8 @@ angular.module('PaginationHelpers', ['Utilities', 'RefreshHelper', 'RefreshRelat } }]) - .factory('RelatedPaginateInit', [ 'RefreshRelated', '$cookieStore', - function(RefreshRelated, $cookieStore) { + .factory('RelatedPaginateInit', [ 'RefreshRelated', '$cookieStore', 'Wait', + function(RefreshRelated, $cookieStore, Wait) { return function(params) { var scope = params.scope; @@ -80,7 +80,7 @@ angular.module('PaginationHelpers', ['Utilities', 'RefreshHelper', 'RefreshRelat new_url += (scope[iterator + 'SearchParams']) ? '&' + scope[iterator + 'SearchParams'] + '&page_size=' + scope[iterator + '_page_size' ] : 'page_size=' + scope[iterator + 'PageSize' ]; Wait('start'); - RefreshRefresh({ scope: scope, set: set, iterator: iterator, url: new_url }); + RefreshRelated({ scope: scope, set: set, iterator: iterator, url: new_url }); } scope.pageIsActive = function(page, iterator) {