diff --git a/awx/ui/client/src/configuration/auth-form/configuration-auth.controller.js b/awx/ui/client/src/configuration/auth-form/configuration-auth.controller.js index 92657b68c9..d8ca6d97f9 100644 --- a/awx/ui/client/src/configuration/auth-form/configuration-auth.controller.js +++ b/awx/ui/client/src/configuration/auth-form/configuration-auth.controller.js @@ -55,6 +55,7 @@ export default [ var formTracker = $scope.$parent.vm.formTracker; var dropdownValue = 'azure'; var activeAuthForm = 'azure'; + let codeInputInitialized = false; // Default active form if ($stateParams.currentTab === '' || $stateParams.currentTab === 'auth') { @@ -244,8 +245,6 @@ export default [ // Flag to avoid re-rendering and breaking Select2 dropdowns on tab switching var dropdownRendered = false; - - function populateLDAPGroupType(flag){ if($scope.$parent.AUTH_LDAP_GROUP_TYPE !== null) { $scope.$parent.AUTH_LDAP_GROUP_TYPE = _.find($scope.$parent.AUTH_LDAP_GROUP_TYPE_options, { value: $scope.$parent.AUTH_LDAP_GROUP_TYPE }); @@ -292,12 +291,24 @@ export default [ populateTacacsProtocol(flag); }); - $scope.$on('codeMirror_populated', function(e, key) { - startCodeMirrors(key); + $scope.$on('$locationChangeStart', (event, url) => { + let parts = url.split('/'); + let tab = parts[parts.length - 1]; + + if (tab === 'auth' && !codeInputInitialized) { + startCodeMirrors(); + codeInputInitialized = true; + } }); $scope.$on('populated', function() { - startCodeMirrors(); + let tab = $stateParams.currentTab; + + if (tab === 'auth') { + startCodeMirrors(); + codeInputInitialized = true; + } + populateLDAPGroupType(false); populateTacacsProtocol(false); }); diff --git a/awx/ui/client/src/configuration/jobs-form/configuration-jobs.controller.js b/awx/ui/client/src/configuration/jobs-form/configuration-jobs.controller.js index ca14b35225..b70e3d71f9 100644 --- a/awx/ui/client/src/configuration/jobs-form/configuration-jobs.controller.js +++ b/awx/ui/client/src/configuration/jobs-form/configuration-jobs.controller.js @@ -8,28 +8,35 @@ export default [ '$scope', '$rootScope', '$state', + '$stateParams', '$timeout', 'ConfigurationJobsForm', 'ConfigurationService', 'ConfigurationUtils', 'CreateSelect2', 'GenerateForm', + 'ParseTypeChange', 'i18n', function( $scope, $rootScope, $state, + $stateParams, $timeout, ConfigurationJobsForm, ConfigurationService, ConfigurationUtils, CreateSelect2, GenerateForm, + ParseTypeChange, i18n ) { - var jobsVm = this; var generator = GenerateForm; var form = ConfigurationJobsForm; + + let tab; + let codeInputInitialized = false; + $scope.$parent.AD_HOC_COMMANDS_options = []; _.each($scope.$parent.configDataResolve.AD_HOC_COMMANDS.default, function(command) { $scope.$parent.AD_HOC_COMMANDS_options.push({ @@ -75,6 +82,18 @@ export default [ // Flag to avoid re-rendering and breaking Select2 dropdowns on tab switching var dropdownRendered = false; + function initializeCodeInput () { + let name = 'AWX_TASK_ENV'; + + ParseTypeChange({ + scope: $scope.$parent, + variable: name, + parseType: 'application/json', + field_id: `configuration_jobs_template_${name}` + }); + + $scope.parseTypeChange('parseType', name); + } function populateAdhocCommand(flag){ $scope.$parent.AD_HOC_COMMANDS = $scope.$parent.AD_HOC_COMMANDS.toString(); @@ -107,22 +126,55 @@ export default [ } } - $scope.$on('AD_HOC_COMMANDS_populated', function(e, data, flag) { - populateAdhocCommand(flag); - }); - - $scope.$on('populated', function() { - populateAdhocCommand(false); - }); - // Fix for bug where adding selected opts causes form to be $dirty and triggering modal // TODO Find better solution for this bug $timeout(function(){ $scope.$parent.configuration_jobs_template_form.$setPristine(); }, 1000); - angular.extend(jobsVm, { + $scope.$on('AD_HOC_COMMANDS_populated', function(e, data, flag) { + populateAdhocCommand(flag); }); + /* + * Controllers for each tab are initialized when configuration is opened. A listener + * on the URL itself is necessary to determine which tab is active. If a non-active + * tab initializes a codemirror, it doesn't display properly until the user navigates + * to the tab and it's been clicked. + */ + $scope.$on('$locationChangeStart', (event, url) => { + let parts = url.split('/'); + tab = parts[parts.length - 1]; + + if (tab === 'jobs' && !codeInputInitialized) { + initializeCodeInput(); + codeInputInitialized = true; + } + }); + + /* + * Necessary to listen for revert clicks and relaunch the codemirror instance. + */ + $scope.$on('codeMirror_populated', () => { + if (tab === 'jobs') { + initializeCodeInput(); + } + }); + + /* + * This event is fired if the user navigates directly to this tab, where the + * $locationChangeStart does not. Watching this and location ensure proper display on + * direct load of this tab or if the user comes from a different tab. + */ + $scope.$on('populated', () => { + tab = $stateParams.currentTab; + + if (tab === 'jobs') { + initializeCodeInput(); + codeInputInitialized = true; + } + + populateAdhocCommand(false); + }); } ]; diff --git a/awx/ui/client/src/configuration/jobs-form/configuration-jobs.form.js b/awx/ui/client/src/configuration/jobs-form/configuration-jobs.form.js index 75a932a512..caa4d522a9 100644 --- a/awx/ui/client/src/configuration/jobs-form/configuration-jobs.form.js +++ b/awx/ui/client/src/configuration/jobs-form/configuration-jobs.form.js @@ -4,76 +4,82 @@ * All Rights Reserved *************************************************/ - export default ['i18n', function(i18n) { - return { - showHeader: false, - name: 'configuration_jobs_template', - showActions: true, +export default ['i18n', function(i18n) { + return { + showHeader: false, + name: 'configuration_jobs_template', + showActions: true, + fields: { + AD_HOC_COMMANDS: { + type: 'select', + ngOptions: 'command.label for command in AD_HOC_COMMANDS_options track by command.value', + reset: 'AD_HOC_COMMANDS', + multiSelect: true + }, + AWX_PROOT_BASE_PATH: { + type: 'text', + reset: 'AWX_PROOT_BASE_PATH', + }, + SCHEDULE_MAX_JOBS: { + type: 'number', + reset: 'SCHEDULE_MAX_JOBS' + }, + AWX_PROOT_SHOW_PATHS: { + type: 'textarea', + reset: 'AWX_PROOT_SHOW_PATHS', + rows: 6 + }, + AWX_ANSIBLE_CALLBACK_PLUGINS: { + type: 'textarea', + reset: 'AWX_ANSIBLE_CALLBACK_PLUGINS', + rows: 6 + }, + AWX_PROOT_HIDE_PATHS: { + type: 'textarea', + reset: 'AWX_PROOT_HIDE_PATHS', + rows: 6 + }, + AWX_PROOT_ENABLED: { + type: 'toggleSwitch', + }, + DEFAULT_JOB_TIMEOUT: { + type: 'text', + reset: 'DEFAULT_JOB_TIMEOUT', + }, + DEFAULT_INVENTORY_UPDATE_TIMEOUT: { + type: 'text', + reset: 'DEFAULT_INVENTORY_UPDATE_TIMEOUT', + }, + DEFAULT_PROJECT_UPDATE_TIMEOUT: { + type: 'text', + reset: 'DEFAULT_PROJECT_UPDATE_TIMEOUT', + }, + ANSIBLE_FACT_CACHE_TIMEOUT: { + type: 'text', + reset: 'ANSIBLE_FACT_CACHE_TIMEOUT', + }, + AWX_TASK_ENV: { + type: 'textarea', + reset: 'AWX_TASK_ENV', + rows: 6, + codeMirror: true, + class: 'Form-textAreaLabel Form-formGroup--fullWidth' + } + }, + buttons: { + reset: { + ngClick: 'vm.resetAllConfirm()', + label: i18n._('Revert all to default'), + class: 'Form-resetAll' + }, + cancel: { + ngClick: 'vm.formCancel()', + }, + save: { + ngClick: 'vm.formSave()', + ngDisabled: true + } + } + }; +}]; - fields: { - AD_HOC_COMMANDS: { - type: 'select', - ngOptions: 'command.label for command in AD_HOC_COMMANDS_options track by command.value', - reset: 'AD_HOC_COMMANDS', - multiSelect: true - }, - AWX_PROOT_BASE_PATH: { - type: 'text', - reset: 'AWX_PROOT_BASE_PATH', - }, - SCHEDULE_MAX_JOBS: { - type: 'number', - reset: 'SCHEDULE_MAX_JOBS' - }, - AWX_PROOT_SHOW_PATHS: { - type: 'textarea', - reset: 'AWX_PROOT_SHOW_PATHS', - rows: 6 - }, - AWX_ANSIBLE_CALLBACK_PLUGINS: { - type: 'textarea', - reset: 'AWX_ANSIBLE_CALLBACK_PLUGINS', - rows: 6 - }, - AWX_PROOT_HIDE_PATHS: { - type: 'textarea', - reset: 'AWX_PROOT_HIDE_PATHS', - rows: 6 - }, - AWX_PROOT_ENABLED: { - type: 'toggleSwitch', - }, - DEFAULT_JOB_TIMEOUT: { - type: 'text', - reset: 'DEFAULT_JOB_TIMEOUT', - }, - DEFAULT_INVENTORY_UPDATE_TIMEOUT: { - type: 'text', - reset: 'DEFAULT_INVENTORY_UPDATE_TIMEOUT', - }, - DEFAULT_PROJECT_UPDATE_TIMEOUT: { - type: 'text', - reset: 'DEFAULT_PROJECT_UPDATE_TIMEOUT', - }, - ANSIBLE_FACT_CACHE_TIMEOUT: { - type: 'text', - reset: 'ANSIBLE_FACT_CACHE_TIMEOUT', - }, - }, - - buttons: { - reset: { - ngClick: 'vm.resetAllConfirm()', - label: i18n._('Revert all to default'), - class: 'Form-resetAll' - }, - cancel: { - ngClick: 'vm.formCancel()', - }, - save: { - ngClick: 'vm.formSave()', - ngDisabled: true - } - } - }; - }];