From 49fd23d84d83005646a58c824f6e5e6b29f1b92c Mon Sep 17 00:00:00 2001 From: chouseknecht Date: Wed, 17 Jul 2013 16:46:26 -0400 Subject: [PATCH] AC-171 Lastest changes for improving select and lookup workflow. Streamlined selection workflow with helpers/Selection.js. Restored the checkbox and fixed the hover background color on Lookup dialogs. Fixed bug in lookup dialog where most recent selection not showing up when user navigates back to same lookup multiple times. Both selection and lookup now work in an Angular fashion by manipulating the data set to affect view changes rather than attempting to manipulate the DOM within the controller. --- awx/ui/static/js/app.js | 3 +- awx/ui/static/js/controllers/Credentials.js | 32 +--------- awx/ui/static/js/controllers/Inventories.js | 2 +- awx/ui/static/js/controllers/JobTemplates.js | 32 +--------- awx/ui/static/js/controllers/Organizations.js | 30 +--------- awx/ui/static/js/controllers/Projects.js | 32 +--------- awx/ui/static/js/controllers/Teams.js | 31 ++-------- awx/ui/static/js/controllers/Users.js | 32 +--------- awx/ui/static/js/helpers/Lookup.js | 32 ++++++---- awx/ui/static/js/helpers/Selection.js | 60 +++++++++++++++++++ awx/ui/static/lib/ansible/list-generator.js | 12 ++-- awx/ui/templates/ui/index.html | 1 + 12 files changed, 110 insertions(+), 189 deletions(-) create mode 100644 awx/ui/static/js/helpers/Selection.js diff --git a/awx/ui/static/js/app.js b/awx/ui/static/js/app.js index 7450b28805..2949170ea0 100644 --- a/awx/ui/static/js/app.js +++ b/awx/ui/static/js/app.js @@ -60,7 +60,8 @@ angular.module('ansible', [ 'EventsHelper', 'ProjectPathHelper', 'md5Helper', - 'AccessHelper' + 'AccessHelper', + 'SelectionHelper' ]) .config(['$routeProvider', function($routeProvider) { $routeProvider. diff --git a/awx/ui/static/js/controllers/Credentials.js b/awx/ui/static/js/controllers/Credentials.js index c9cf9d0334..0898fce615 100644 --- a/awx/ui/static/js/controllers/Credentials.js +++ b/awx/ui/static/js/controllers/Credentials.js @@ -12,7 +12,7 @@ function CredentialsList ($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, CredentialList, GenerateList, LoadBreadCrumbs, Prompt, SearchInit, PaginateInit, ReturnToCaller, - ClearScope, ProcessErrors, GetBasePath) + ClearScope, ProcessErrors, GetBasePath, SelectionInit) { ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior //scope. @@ -24,16 +24,7 @@ function CredentialsList ($scope, $rootScope, $location, $log, $routeParams, Res var scope = view.inject(list, { mode: mode }); // Inject our view scope.selected = []; - if (scope.PostRefreshRemove) { - scope.PostRefreshRemove(); - } - scope.PostRefreshRemove = scope.$on('PostRefresh', function() { - $("tr.success").each(function(index) { - // Make sure no rows have a green background - var ngc = $(this).attr('ng-class'); - scope[ngc] = ""; - }); - }); + SelectionInit({ scope: scope, list: list }); SearchInit({ scope: scope, set: 'credentials', list: list, url: defaultUrl }); PaginateInit({ scope: scope, list: list, url: defaultUrl }); @@ -125,28 +116,11 @@ function CredentialsList ($scope, $rootScope, $location, $log, $routeParams, Res ReturnToCaller(); } } - - scope.toggle_credential = function(id) { - if (scope[list.iterator + "_" + id + "_class"] == "success") { - scope[list.iterator + "_" + id + "_class"] = ""; - document.getElementById('check_' + id).checked = false; - if (scope.selected.indexOf(id) > -1) { - scope.selected.splice(scope.selected.indexOf(id),1); - } - } - else { - scope[list.iterator + "_" + id + "_class"] = "success"; - document.getElementById('check_' + id).checked = true; - if (scope.selected.indexOf(id) == -1) { - scope.selected.push(id); - } - } - } } CredentialsList.$inject = [ '$scope', '$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'CredentialList', 'GenerateList', 'LoadBreadCrumbs', 'Prompt', 'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope', 'ProcessErrors', - 'GetBasePath']; + 'GetBasePath', 'SelectionInit']; function CredentialsAdd ($scope, $rootScope, $compile, $location, $log, $routeParams, CredentialForm, diff --git a/awx/ui/static/js/controllers/Inventories.js b/awx/ui/static/js/controllers/Inventories.js index 845e98ace4..e2fdc8b115 100644 --- a/awx/ui/static/js/controllers/Inventories.js +++ b/awx/ui/static/js/controllers/Inventories.js @@ -294,7 +294,7 @@ function InventoriesEdit ($scope, $rootScope, $compile, $location, $log, $routeP LookUpInit({ scope: scope, form: form, - current_item: (scope.organization) ? scope.organization : null, + current_item: (scope.organization !== undefined) ? scope.organization : null, list: OrganizationList, field: 'organization' }); diff --git a/awx/ui/static/js/controllers/JobTemplates.js b/awx/ui/static/js/controllers/JobTemplates.js index ebb9443625..3fde23f012 100644 --- a/awx/ui/static/js/controllers/JobTemplates.js +++ b/awx/ui/static/js/controllers/JobTemplates.js @@ -25,18 +25,6 @@ function JobTemplatesList ($scope, $rootScope, $location, $log, $routeParams, Re var scope = view.inject(list, { mode: mode }); $rootScope.flashMessage = null; scope.selected = []; - - if (scope.PostRefreshRemove) { - scope.PostRefreshRemove(); - } - scope.PostRefreshRemove = scope.$on('PostRefresh', function() { - $("tr.success").each(function(index) { - // Make sure no rows have a green background - var ngc = $(this).attr('ng-class'); - scope[ngc] = ""; - }); - }); - SearchInit({ scope: scope, set: 'job_templates', list: list, url: defaultUrl }); PaginateInit({ scope: scope, list: list, url: defaultUrl }); @@ -129,23 +117,6 @@ function JobTemplatesList ($scope, $rootScope, $location, $log, $routeParams, Re } } - scope.toggle_job_template = function(id) { - if (scope[list.iterator + "_" + id + "_class"] == "success") { - scope[list.iterator + "_" + id + "_class"] = ""; - document.getElementById('check_' + id).checked = false; - if (scope.selected.indexOf(id) > -1) { - scope.selected.splice(scope.selected.indexOf(id),1); - } - } - else { - scope[list.iterator + "_" + id + "_class"] = "success"; - document.getElementById('check_' + id).checked = true; - if (scope.selected.indexOf(id) == -1) { - scope.selected.push(id); - } - } - } - scope.submitJob = function(id) { SubmitJob({ scope: scope, id: id }); } @@ -347,6 +318,9 @@ function JobTemplatesEdit ($scope, $rootScope, $compile, $location, $log, $route for (var i=0; i < data.length; i++) { scope.playbook_options.push(data[i]); } + if (!scope.$$phase) { + scope.$digest(); + } }) .error( function(data, status, headers, config) { ProcessErrors(scope, data, status, form, diff --git a/awx/ui/static/js/controllers/Organizations.js b/awx/ui/static/js/controllers/Organizations.js index 9d98196bdf..86ff5b82cb 100644 --- a/awx/ui/static/js/controllers/Organizations.js +++ b/awx/ui/static/js/controllers/Organizations.js @@ -27,18 +27,7 @@ function OrganizationsList ($scope, $rootScope, $location, $log, Rest, Alert, Lo $rootScope.flashMessage = null; LoadBreadCrumbs(); - - if (scope.PostRefreshRemove) { - scope.PostRefreshRemove(); - } - scope.PostRefreshRemove = scope.$on('PostRefresh', function() { - $("tr.success").each(function(index) { - // Make sure no rows have a green background - var ngc = $(this).attr('ng-class'); - scope[ngc] = ""; - }); - }); - + // Initialize search and paginate pieces and load data SearchInit({ scope: scope, set: list.name, list: list, url: defaultUrl }); PaginateInit({ scope: scope, list: list, url: defaultUrl }); @@ -76,23 +65,6 @@ function OrganizationsList ($scope, $rootScope, $location, $log, Rest, Alert, Lo action: action }); } - - scope.toggle_organization = function(id) { - if (scope[list.iterator + "_" + id + "_class"] == "success") { - scope[list.iterator + "_" + id + "_class"] = ""; - document.getElementById('check_' + id).checked = false; - if (scope.selected.indexOf(id) > -1) { - scope.selected.splice(scope.selected.indexOf(id),1); - } - } - else { - scope[list.iterator + "_" + id + "_class"] = "success"; - document.getElementById('check_' + id).checked = true; - if (scope.selected.indexOf(id) == -1) { - scope.selected.push(id); - } - } - } } OrganizationsList.$inject=[ '$scope', '$rootScope', '$location', '$log', 'Rest', 'Alert', 'LoadBreadCrumbs', 'Prompt', diff --git a/awx/ui/static/js/controllers/Projects.js b/awx/ui/static/js/controllers/Projects.js index b395e94d69..86062fea79 100644 --- a/awx/ui/static/js/controllers/Projects.js +++ b/awx/ui/static/js/controllers/Projects.js @@ -12,7 +12,7 @@ function ProjectsList ($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, ProjectList, GenerateList, LoadBreadCrumbs, Prompt, SearchInit, PaginateInit, ReturnToCaller, - ClearScope, ProcessErrors, GetBasePath) + ClearScope, ProcessErrors, GetBasePath, SelectionInit) { ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior //scope. @@ -25,16 +25,7 @@ function ProjectsList ($scope, $rootScope, $location, $log, $routeParams, Rest, scope.selected = []; $rootScope.flashMessage = null; - if (scope.PostRefreshRemove) { - scope.PostRefreshRemove(); - } - scope.PostRefreshRemove = scope.$on('PostRefresh', function() { - $("tr.success").each(function(index) { - // Make sure no rows have a green background - var ngc = $(this).attr('ng-class'); - scope[ngc] = ""; - }); - }); + SelectionInit({ scope: scope, list: list }); SearchInit({ scope: scope, set: 'projects', list: list, url: defaultUrl }); PaginateInit({ scope: scope, list: list, url: defaultUrl }); @@ -128,28 +119,11 @@ function ProjectsList ($scope, $rootScope, $location, $log, $routeParams, Rest, ReturnToCaller(1); } } - - scope.toggle_project = function(id) { - if (scope[list.iterator + "_" + id + "_class"] == "success") { - scope[list.iterator + "_" + id + "_class"] = ""; - document.getElementById('check_' + id).checked = false; - if (scope.selected.indexOf(id) > -1) { - scope.selected.splice(scope.selected.indexOf(id),1); - } - } - else { - scope[list.iterator + "_" + id + "_class"] = "success"; - document.getElementById('check_' + id).checked = true; - if (scope.selected.indexOf(id) == -1) { - scope.selected.push(id); - } - } - } } ProjectsList.$inject = [ '$scope', '$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'ProjectList', 'GenerateList', 'LoadBreadCrumbs', 'Prompt', 'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope', 'ProcessErrors', - 'GetBasePath' ]; + 'GetBasePath', 'SelectionInit']; function ProjectsAdd ($scope, $rootScope, $compile, $location, $log, $routeParams, ProjectsForm, diff --git a/awx/ui/static/js/controllers/Teams.js b/awx/ui/static/js/controllers/Teams.js index a9f0113a32..1a0292eabb 100644 --- a/awx/ui/static/js/controllers/Teams.js +++ b/awx/ui/static/js/controllers/Teams.js @@ -12,7 +12,7 @@ function TeamsList ($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, TeamList, GenerateList, LoadBreadCrumbs, Prompt, SearchInit, PaginateInit, ReturnToCaller, - ClearScope, ProcessErrors, SetTeamListeners, GetBasePath) + ClearScope, ProcessErrors, SetTeamListeners, GetBasePath, SelectionInit) { ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior //scope. @@ -23,7 +23,9 @@ function TeamsList ($scope, $rootScope, $location, $log, $routeParams, Rest, Ale var mode = (paths[0] == 'teams') ? 'edit' : 'select'; // if base path 'teams', we're here to add/edit teams var scope = view.inject(list, { mode: mode }); // Inject our view scope.selected = []; - + + SelectionInit({ scope: scope, list: list }); + if (scope.PostRefreshRemove) { scope.PostRefreshRemove(); } @@ -32,12 +34,6 @@ function TeamsList ($scope, $rootScope, $location, $log, $routeParams, Rest, Ale for( var i=0; i < scope.teams.length; i++) { scope.teams[i].organization_name = scope.teams[i].summary_fields.organization.name; } - - $("tr.success").each(function(index) { - // Make sure no rows have a green background - var ngc = $(this).attr('ng-class'); - scope[ngc] = ""; - }); }); //SetTeamListeners({ scope: scope, set: 'teams', iterator: list.iterator }); @@ -145,28 +141,11 @@ function TeamsList ($scope, $rootScope, $location, $log, $routeParams, Rest, Ale ReturnToCaller(); } } - - scope.toggle_team = function(id) { - if (scope[list.iterator + "_" + id + "_class"] == "success") { - scope[list.iterator + "_" + id + "_class"] = ""; - document.getElementById('check_' + id).checked = false; - if (scope.selected.indexOf(id) > -1) { - scope.selected.splice(scope.selected.indexOf(id),1); - } - } - else { - scope[list.iterator + "_" + id + "_class"] = "success"; - document.getElementById('check_' + id).checked = true; - if (scope.selected.indexOf(id) == -1) { - scope.selected.push(id); - } - } - } } TeamsList.$inject = [ '$scope', '$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'TeamList', 'GenerateList', 'LoadBreadCrumbs', 'Prompt', 'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope', 'ProcessErrors', - 'SetTeamListeners', 'GetBasePath' ]; + 'SetTeamListeners', 'GetBasePath', 'SelectionInit']; function TeamsAdd ($scope, $rootScope, $compile, $location, $log, $routeParams, TeamForm, diff --git a/awx/ui/static/js/controllers/Users.js b/awx/ui/static/js/controllers/Users.js index 8a5e0a15b2..6c204c4de4 100644 --- a/awx/ui/static/js/controllers/Users.js +++ b/awx/ui/static/js/controllers/Users.js @@ -12,7 +12,7 @@ function UsersList ($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, UserList, GenerateList, LoadBreadCrumbs, Prompt, SearchInit, PaginateInit, - ReturnToCaller, ClearScope, ProcessErrors, GetBasePath) + ReturnToCaller, ClearScope, ProcessErrors, GetBasePath, SelectionInit) { ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior //scope. @@ -30,18 +30,9 @@ function UsersList ($scope, $rootScope, $location, $log, $routeParams, Rest, PaginateInit({ scope: scope, list: list, url: defaultUrl }); scope.search(list.iterator); - LoadBreadCrumbs(); - if (scope.PostRefreshRemove) { - scope.PostRefreshRemove(); - } - scope.PostRefreshRemove = scope.$on('PostRefresh', function() { - // Remove the 'success' class, which makes green or selected rows, from each row - for (var i=0; i < scope[list.name].length; i++) { - scope[list.iterator + '_' + scope[list.name][i].id + '_class'] = ''; - } - }); + SelectionInit({ scope: scope, list: list }); scope.addUser = function() { $location.path($location.path() + '/add'); @@ -88,7 +79,6 @@ function UsersList ($scope, $rootScope, $location, $log, $routeParams, Rest, // calls are finished. if (scope.queue.length == scope.selected.length) { // All the api calls finished - $('input[type="checkbox"]').prop("checked",false); scope.selected = []; var errors = 0; for (var i=0; i < scope.queue.length; i++) { @@ -133,27 +123,11 @@ function UsersList ($scope, $rootScope, $location, $log, $routeParams, Rest, } } - scope.toggle_user = function(id) { - if (scope[list.iterator + "_" + id + "_class"] == "success") { - scope[list.iterator + "_" + id + "_class"] = ""; - document.getElementById('check_' + id).checked = false; - if (scope.selected.indexOf(id) > -1) { - scope.selected.splice(scope.selected.indexOf(id),1); - } - } - else { - scope[list.iterator + "_" + id + "_class"] = "success"; - document.getElementById('check_' + id).checked = true; - if (scope.selected.indexOf(id) == -1) { - scope.selected.push(id); - } - } - } } UsersList.$inject = [ '$scope', '$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'UserList', 'GenerateList', 'LoadBreadCrumbs', 'Prompt', 'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope', 'ProcessErrors', - 'GetBasePath' ]; + 'GetBasePath', 'SelectionInit']; function UsersAdd ($scope, $rootScope, $compile, $location, $log, $routeParams, UserForm, diff --git a/awx/ui/static/js/helpers/Lookup.js b/awx/ui/static/js/helpers/Lookup.js index dab316a453..b287946d11 100644 --- a/awx/ui/static/js/helpers/Lookup.js +++ b/awx/ui/static/js/helpers/Lookup.js @@ -43,7 +43,7 @@ angular.module('LookUpHelper', [ 'RestServices', 'Utilities', 'SearchHelper', 'P var found = false; var name; for (var i=0; i < listScope[list.name].length; i++) { - if (listScope[list.iterator + "_" + listScope[list.name][i].id + "_class"] == "success") { + if (listScope[list.name][i]['checked'] == '1') { found = true; scope[field] = listScope[list.name][i].id; if (scope[form.name + '_form'] && form.fields[field] && form.fields[field].sourceModel) { @@ -72,23 +72,31 @@ angular.module('LookUpHelper', [ 'RestServices', 'Utilities', 'SearchHelper', 'P } listScope['toggle_' + list.iterator] = function(id) { - // when user clicks a row, remove 'success' class from all rows except clicked-on row - if (listScope[list.name]) { - for (var i=0; i < listScope[list.name].length; i++) { - listScope[list.iterator + "_" + listScope[list.name][i].id + "_class"] = ""; - } - } - if (id != null && id != undefined) { - listScope[list.iterator + "_" + id + "_class"] = "success"; + for (var i=0; i < scope[list.name].length; i++) { + if (listScope[list.name][i]['id'] == id) { + listScope[list.name][i]['checked'] = '1'; + listScope[list.name][i]['success_class'] = 'success'; + } + else { + listScope[list.name][i]['checked'] = '0'; + listScope[list.name][i]['success_class'] = ''; + } } } SearchInit({ scope: listScope, set: list.name, list: list, url: defaultUrl }); PaginateInit({ scope: listScope, list: list, url: defaultUrl, mode: 'lookup' }); - listScope.search(list.iterator); - if (current_item) { - listScope['toggle_' + list.iterator](current_item); + + // If user made a selection previously, mark it as selected when modal loads + if (listScope.lookupPostRefreshRemove) { + listScope.lookupPostRefreshRemove(); } + listScope.lookupPostRefreshRemove = scope.$on('PostRefresh', function() { + listScope['toggle_' + list.iterator](scope[field]); + }); + + listScope.search(list.iterator); + } } }]); diff --git a/awx/ui/static/js/helpers/Selection.js b/awx/ui/static/js/helpers/Selection.js new file mode 100644 index 0000000000..e5aba47ac6 --- /dev/null +++ b/awx/ui/static/js/helpers/Selection.js @@ -0,0 +1,60 @@ +/********************************************* + * Copyright (c) 2013 AnsibleWorks, Inc. + * + * SelectionHelper + * Used in list controllers where the list might also be used as a selection list. + * + * SelectionInit( { + * scope: , + * list: + * }) + */ + +angular.module('SelectionHelper', []) + .factory('SelectionInit', [ function() { + return function(params) { + + var scope = params.scope; // form scope + var list = params.list; // list object + + scope.selected = []; //array of selected row IDs + + // toggle row selection + scope['toggle_' + list.iterator] = function(id) { + for (var i=0; i < scope[list.name].length; i++) { + if (scope[list.name][i]['id'] == id) { + if (scope[list.name][i]['checked'] == '0') { + // select the row + scope[list.name][i]['checked'] = '1'; + scope[list.name][i]['success_class'] = 'success'; + if (scope.selected.indexOf(id) == -1) { + // add id to the array + scope.selected.push(id); + } + } + else { + // unselect the row + scope[list.name][i]['checked'] = '0'; + scope[list.name][i]['success_class'] = ''; + if (scope.selected.indexOf(id) > -1) { + // remove id from the array + scope.selected.splice(scope.selected.indexOf(id),1); + } + } + } + } + } + + // Initialize our data set after a refresh + if (scope.SelectPostRefreshRemove) { + scope.SelectPostRefreshRemove(); + } + scope.SelectPostRefreshRemove = scope.$on('PostRefresh', function() { + scope.selected = []; + for (var i=0; i < scope[list.name].length; i++) { + scope[list.name][i]['checked'] = '0'; + scope[list.name][i]['success_class'] = ''; + } + }); + } + }]); \ No newline at end of file diff --git a/awx/ui/static/lib/ansible/list-generator.js b/awx/ui/static/lib/ansible/list-generator.js index 4c87d398c4..ce124d9b6c 100644 --- a/awx/ui/static/lib/ansible/list-generator.js +++ b/awx/ui/static/lib/ansible/list-generator.js @@ -201,7 +201,7 @@ angular.module('ListGenerator', ['GeneratorHelpers']) html += "\n"; } } - if (options.mode == 'select') { + if (options.mode == 'select' || options.mode == 'lookup') { html += "Select"; } else if (options.mode == 'edit') { @@ -213,7 +213,7 @@ angular.module('ListGenerator', ['GeneratorHelpers']) // table body html += "\n"; html += ""; + if (options.mode == 'select' || options.mode == 'lookup') { + //html += ""; + + html += ""; + //ng-click=\"toggle_" + list.iterator + // "({{ " + list.iterator + ".id }}, true)\" } diff --git a/awx/ui/templates/ui/index.html b/awx/ui/templates/ui/index.html index c9f02f0076..f34a2364a2 100644 --- a/awx/ui/templates/ui/index.html +++ b/awx/ui/templates/ui/index.html @@ -87,6 +87,7 @@ + {% endif %}