From 0d2417e4aeb77cb799ad5e168e7489fbd40b044f Mon Sep 17 00:00:00 2001 From: Chris Houseknecht Date: Thu, 9 Jan 2014 22:45:27 +0000 Subject: [PATCH] Inventory refactor: inventory sync icon turns into red cancel icon upon job submission. cancel sync now works. Refresh process emits on completion. Created Find() utility to search an array of objects. --- awx/ui/static/js/controllers/Inventories.js | 22 ++- awx/ui/static/js/helpers/Groups.js | 201 ++++++++++---------- awx/ui/static/js/helpers/Hosts.js | 21 +- awx/ui/static/js/helpers/JobSubmission.js | 33 ++-- awx/ui/static/js/lists/InventoryGroups.js | 19 +- awx/ui/static/less/ansible-ui.less | 3 + awx/ui/static/lib/ansible/InventoryTree.js | 7 +- awx/ui/static/lib/ansible/Utilities.js | 31 ++- awx/ui/static/lib/ansible/list-generator.js | 1 + 9 files changed, 190 insertions(+), 148 deletions(-) diff --git a/awx/ui/static/js/controllers/Inventories.js b/awx/ui/static/js/controllers/Inventories.js index 916a01650e..cee1fd14d9 100644 --- a/awx/ui/static/js/controllers/Inventories.js +++ b/awx/ui/static/js/controllers/Inventories.js @@ -318,7 +318,7 @@ InventoriesAdd.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$lo function InventoriesEdit ($scope, $location, $routeParams, $compile, GenerateList, ClearScope, InventoryGroups, InventoryHosts, BuildTree, Wait, GetSyncStatusMsg, InjectHosts, HostsReload, GroupsAdd, GroupsEdit, GroupsDelete, Breadcrumbs, LoadBreadCrumbs, Empty, - Rest, ProcessErrors, InventoryUpdate, Alert, ToggleChildren, ViewUpdateStatus) + Rest, ProcessErrors, InventoryUpdate, Alert, ToggleChildren, ViewUpdateStatus, GroupsCancelUpdate) { ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior //scope. @@ -362,16 +362,15 @@ function InventoriesEdit ($scope, $location, $routeParams, $compile, GenerateLis // Add hosts view $scope.show_failures = false; InjectHosts({ scope: $scope, inventory_id: $scope.inventory_id, tree_id: $scope.selected_tree_id, group_id: $scope.selected_group_id }); - }); if ($scope.removeGroupTreeRefreshed) { $scope.removeGroupTreeRefreshed(); } - $scope.removeGroupTreeRefreshed = $scope.$on('groupTreeRefreshed', function(e, inventory_name, groups) { + $scope.removeGroupTreeRefreshed = $scope.$on('groupTreeRefreshed', function(e, inventory_name, groups, emit) { // Called after tree data is reloaded on refresh button click. // Reselect the preveiously selected group node, causing host view to refresh. - $scope.showHosts($scope.selected_tree_id, $scope.selected_group_id); + $scope.showHosts($scope.selected_tree_id, $scope.selected_group_id, false, emit); }); if ($scope.removeGroupDeleteCompleted) { @@ -382,7 +381,7 @@ function InventoriesEdit ($scope, $location, $routeParams, $compile, GenerateLis BuildTree({ scope: $scope, inventory_id: $scope.inventory_id, refresh: true }); }); - $scope.showHosts = function(tree_id, group_id, show_failures) { + $scope.showHosts = function(tree_id, group_id, show_failures, emit) { // Clicked on group if (tree_id !== null) { Wait('start'); @@ -401,7 +400,7 @@ function InventoriesEdit ($scope, $location, $routeParams, $compile, GenerateLis $scope.groups[i].active_class = ''; } } - HostsReload({ scope: $scope, group_id: group_id, tree_id: tree_id, inventory_id: $scope.inventory_id }); + HostsReload({ scope: $scope, group_id: group_id, tree_id: tree_id, inventory_id: $scope.inventory_id, emit: emit }); } } @@ -445,7 +444,11 @@ function InventoriesEdit ($scope, $location, $routeParams, $compile, GenerateLis } break; } + } } + + $scope.cancelUpdate = function(id) { + GroupsCancelUpdate({ scope: $scope, id: id }); } $scope.toggle = function(id) { @@ -453,9 +456,10 @@ function InventoriesEdit ($scope, $location, $routeParams, $compile, GenerateLis ToggleChildren({ scope: $scope, list: list, id: id }); } - $scope.refreshGroups = function() { + $scope.refreshGroups = function(emit) { // Refresh the tree data when refresh button cicked - BuildTree({ scope: $scope, inventory_id: $scope.inventory_id, refresh: true }); + // Pass a string label value, if you want to attach scope.$on() to the completion of the refresh + BuildTree({ scope: $scope, inventory_id: $scope.inventory_id, refresh: true, emit: emit }); } $scope.viewUpdateStatus = function(id) { @@ -474,6 +478,6 @@ function InventoriesEdit ($scope, $location, $routeParams, $compile, GenerateLis InventoriesEdit.$inject = [ '$scope', '$location', '$routeParams', '$compile', 'GenerateList', 'ClearScope', 'InventoryGroups', 'InventoryHosts', 'BuildTree', 'Wait', 'GetSyncStatusMsg', 'InjectHosts', 'HostsReload', 'GroupsAdd', 'GroupsEdit', 'GroupsDelete', 'Breadcrumbs', 'LoadBreadCrumbs', 'Empty', 'Rest', 'ProcessErrors', 'InventoryUpdate', 'Alert', 'ToggleChildren', - 'ViewUpdateStatus' + 'ViewUpdateStatus', 'GroupsCancelUpdate' ]; diff --git a/awx/ui/static/js/helpers/Groups.js b/awx/ui/static/js/helpers/Groups.js index e8d850c7a9..87fb816939 100644 --- a/awx/ui/static/js/helpers/Groups.js +++ b/awx/ui/static/js/helpers/Groups.js @@ -449,88 +449,7 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', ' ClickNode({ selector: '#inventory-tree li[data-group-id="' + group_id + '"]' }); } - if (scope.removeCancelUpdate) { - scope.removeCancelUpdate(); - } - scope.removeCancelUpdate = scope.$on('Cancel_Update', function(e, url) { - // Cancel the project update process - Rest.setUrl(url) - Rest.post() - .success( function(data, status, headers, config) { - Alert('SCM Update Cancel', 'Your request to cancel the update was submitted to the task maanger.', 'alert-info'); - scope.refresh(); - }) - .error( function(data, status, headers, config) { - ProcessErrors(scope, data, status, null, - { hdr: 'Error!', msg: 'Call to ' + url + ' failed. POST status: ' + status }); - }); - }); - - if (scope.removeCheckCancel) { - scope.removeCheckCancel(); - } - scope.removeCheckCancel = scope.$on('Check_Cancel', function(e, last_update, current_update) { - // Check that we 'can' cancel the update - var url = (current_update) ? current_update : last_update; - url += 'cancel/'; - Rest.setUrl(url); - Rest.get() - .success( function(data, status, headers, config) { - if (data.can_cancel) { - scope.$emit('Cancel_Update', url); - } - else { - Alert('Cancel Not Allowed', 'Either you do not have access or the Inventory update process completed. Click the Refresh button to' + - ' view the latest status.', 'alert-info'); - } - }) - .error( function(data, status, headers, config) { - ProcessErrors(scope, data, status, null, - { hdr: 'Error!', msg: 'Call to ' + url + ' failed. GET status: ' + status }); - }); - }); - - scope.cancelUpdate = function(id, name) { - // Cancel the update process - var group; - var found = false; - for (var i=0; i < scope.groups.length; i++) { - if (scope.groups[i].id == id) { - group = scope.groups[i]; - found = true; - break; - } - } - if (group.summary_fields.inventory_source.source !== '' && - group.summary_fields.inventory_source.source !== null) { - // the group has a source - if (group.summary_fields.inventory_source.status == 'updating' || - group.summary_fields.inventory_source.status == 'pending') { - // there is an update currently running - Rest.setUrl(group.related.inventory_source); - Rest.get() - .success( function(data, status, headers, config) { - scope.$emit('Check_Cancel', data.related.last_update, data.related.current_update); - }) - .error( function(data, status, headers, config) { - ProcessErrors(scope, data, status, null, - { hdr: 'Error!', msg: 'Call to ' + group.related.inventory_source + ' failed. GET status: ' + status }); - }); - } - } - // The button appears disabled, so act like it is and do not respond to a click. - /* else { - Alert('Update Not Found', 'An Inventory update does not appear to be running for group: ' + group.name + '. Click the Refresh ' + - 'button to view the latet status.', 'alert-info'); - } - } - else { - Alert('Missing Configuration', 'The selected group is not configured for updates. You must first edit the group and provide external Source settings ' + - 'before attempting an update.', 'alert-info'); - }*/ - - } - + // Respond to refresh button scope.refresh = function() { /*scope.search(list.iterator, false, true); @@ -632,6 +551,84 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', ' } }]) + + // Cancel a pending or running inventory sync + .factory('GroupsCancelUpdate', ['Rest', 'ProcessErrors', 'Alert', 'Wait', 'Find', + function(Rest, ProcessErrors, Alert, Wait, Find) { + return function(params) { + + var scope = params.scope; + var id = params.tree_id; + + if (scope.removeCancelUpdate) { + scope.removeCancelUpdate(); + } + scope.removeCancelUpdate = scope.$on('CancelUpdate', function(e, url) { + // Cancel the update process + Rest.setUrl(url) + Rest.post() + .success( function(data, status, headers, config) { + Wait('stop'); + Alert('Inventory Sync Cancelled', 'Your request to cancel the update was submitted to the task maanger. ' + + 'Click the to check the sync status.', 'alert-info'); + }) + .error( function(data, status, headers, config) { + Wait('stop'); + ProcessErrors(scope, data, status, null, + { hdr: 'Error!', msg: 'Call to ' + url + ' failed. POST status: ' + status }); + }); + }); + + if (scope.removeCheckCancel) { + scope.removeCheckCancel(); + } + scope.removeCheckCancel = scope.$on('CheckCancel', function(e, last_update, current_update) { + // Check that we have access to cancelling an update + var url = (current_update) ? current_update : last_update; + url += 'cancel/'; + Rest.setUrl(url); + Rest.get() + .success( function(data, status, headers, config) { + if (data.can_cancel) { + scope.$emit('CancelUpdate', url); + } + else { + Wait('stop'); + Alert('Cancel Inventory Sync', 'Either you do not have access or the sync process completed. ' + + 'Click the to view the latest status.', 'alert-info'); + } + }) + .error( function(data, status, headers, config) { + Wait('stop'); + ProcessErrors(scope, data, status, null, + { hdr: 'Error!', msg: 'Call to ' + url + ' failed. GET status: ' + status }); + }); + }); + + // Cancel the update process + var group = Find({ list: scope.groups, key: 'id', val: id }); + if (group && (group.status == 'updating' || group.status == 'pending')) { + // We found the group, and there is a running update + Wait('start'); + Rest.setUrl(group.related.inventory_source); + Rest.get() + .success( function(data, status, headers, config) { + scope.$emit('CheckCancel', data.related.last_update, data.related.current_update); + }) + .error( function(data, status, headers, config) { + Wait('stop'); + ProcessErrors(scope, data, status, null, + { hdr: 'Error!', msg: 'Call to ' + group.related.inventory_source + ' failed. GET status: ' + status }); + }); + } + else { + Alert('Cancel Inventory Sync', 'The sync process completed. Click the to' + + ' view the latest status.', 'alert-info'); + } + + } + }]) + .factory('GroupsAdd', ['$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'GroupForm', 'GenerateForm', 'Prompt', 'ProcessErrors', 'GetBasePath', 'ParseTypeChange', 'GroupsEdit', 'ClickNode', 'Wait', 'GetChoices', 'GetSourceTypeOptions', 'LookUpInit', 'BuildTree', 'SourceChange', @@ -1344,24 +1341,24 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', ' node = scope.groups[i]; } } - if (node.parent != 0) { - for (var i=0; i < scope.groups.length; i++) { - if (scope.groups[i].id == node.parent) { - parent = scope.groups[i]; - parent_name = scope.groups[i].name; - } - } - } - else { - parent_name = scope.inventory_name; - } + //if (node.parent != 0) { + // for (var i=0; i < scope.groups.length; i++) { + // if (scope.groups[i].id == node.parent) { + // parent = scope.groups[i]; + // parent_name = scope.groups[i].name; + // } + // } + // } + // else { + // parent_name = scope.inventory_name; + // } - if (parent) { - url = GetBasePath('base') + 'groups/' + parent.group_id + '/children/'; - } - else { + //if (parent) { + // url = GetBasePath('base') + 'groups/' + parent.group_id + '/children/'; + //} + //else { url = GetBasePath('inventory') + inventory_id + '/groups/'; - } + //} var action_to_take = function() { $('#prompt-modal').on('hidden.bs.modal', function(){ Wait('start'); }); $('#prompt-modal').modal('hide'); @@ -1378,10 +1375,10 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', ' }); }; - Prompt({ hdr: 'Delete Group', body: '

Are you sure you want to remove group ' + node.name + - ' from ' + parent_name + '?

', action: action_to_take, - 'class': 'btn-danger' }); - + Prompt({ hdr: 'Delete Group', body: '

Are you sure you want to delete group ' + node.name + '?

', + action: action_to_take, 'class': 'btn-danger' }); + + //' from ' + parent_name + ' //Force binds to work. Not working usual way. //$('#prompt-header').text('Delete Group'); //$('#prompt-body').html('

Are you sure you want to remove group ' + $(obj).attr('data-name') + ' from group ' + diff --git a/awx/ui/static/js/helpers/Hosts.js b/awx/ui/static/js/helpers/Hosts.js index 6a2905f71d..21276e3462 100644 --- a/awx/ui/static/js/helpers/Hosts.js +++ b/awx/ui/static/js/helpers/Hosts.js @@ -14,20 +14,31 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H ]) - .factory('HostsReload', [ 'Empty', 'InventoryHosts', 'GetBasePath', 'SearchInit', 'PaginateInit', - function(Empty, InventoryHosts, GetBasePath, SearchInit, PaginateInit) { + .factory('HostsReload', [ 'Empty', 'InventoryHosts', 'GetBasePath', 'SearchInit', 'PaginateInit', 'Wait', + function(Empty, InventoryHosts, GetBasePath, SearchInit, PaginateInit, Wait) { return function(params) { var scope = params.scope; var group_id = params.group_id; var tree_id = params.tree_id var inventory_id = params.inventory_id; + var emit = params.emit; var url = ( !Empty(group_id) ) ? GetBasePath('groups') + group_id + '/all_hosts/' : GetBasePath('inventory') + inventory_id + '/hosts/'; scope.search_place_holder='Search ' + scope.selected_group_name; + if (scope.removePostRefresh) { + scope.removePostRefresh(); + } + scope.removePostRefresh = scope.$on('PostRefresh', function(e) { + Wait('stop'); + if (emit) { + scope.$emit(emit); + } + }); + SearchInit({ scope: scope, set: 'hosts', list: InventoryHosts, url: url }); PaginateInit({ scope: scope, list: InventoryHosts, url: url }); scope.search(InventoryHosts.iterator); @@ -42,10 +53,14 @@ angular.module('HostsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', 'H var inventory_id = params.inventory_id; var group_id = params.group_id; var tree_id = params.tree_id; + var emit = params.emit; + // Inject the list html var generator = GenerateList; generator.inject(InventoryHosts, { scope: scope, mode: 'edit', id: 'hosts-container', breadCrumbs: false, searchSize: 'col-lg-6 col-md-6 col-sm-6' }); - HostsReload({ scope: scope, group_id: group_id, tree_id: tree_id, inventory_id: inventory_id }); + + // Load data + HostsReload({ scope: scope, group_id: group_id, tree_id: tree_id, inventory_id: inventory_id, emit: emit }); } }]) diff --git a/awx/ui/static/js/helpers/JobSubmission.js b/awx/ui/static/js/helpers/JobSubmission.js index 2e919407e5..6cca02c1f7 100644 --- a/awx/ui/static/js/helpers/JobSubmission.js +++ b/awx/ui/static/js/helpers/JobSubmission.js @@ -9,8 +9,7 @@ angular.module('JobSubmissionHelper', [ 'RestServices', 'Utilities', 'Credential .factory('PromptPasswords', ['CredentialForm', 'JobTemplateForm', '$compile', 'Rest', '$location', 'ProcessErrors', 'GetBasePath', 'Alert', 'Empty', 'Wait', - function(CredentialForm, JobTemplateForm, $compile, Rest, $location, ProcessErrors, GetBasePath, Alert, Empty, - Wait) { + function(CredentialForm, JobTemplateForm, $compile, Rest, $location, ProcessErrors, GetBasePath, Alert, Empty, Wait) { return function(params) { var scope = params.scope; @@ -79,7 +78,6 @@ angular.module('JobSubmissionHelper', [ 'RestServices', 'Utilities', 'Credential Rest.setUrl(start_url); Rest.post(pswd) .success( function(data, status, headers, config) { - Wait('stop'); scope.$emit('UpdateSubmitted','started'); if (form.name == 'credential') { navigate(false); @@ -332,6 +330,7 @@ angular.module('JobSubmissionHelper', [ 'RestServices', 'Utilities', 'Credential } scope.removeUpdateSubmitted = scope.$on('UpdateSubmitted', function(e, action) { // Refresh the project list after update request submitted + Wait('stop'); Alert('Update Started', 'The request to start the SCM update process was submitted. ' + 'To monitor the update status, refresh the page by clicking the Refresh button.', 'alert-info'); scope.refresh(); @@ -415,29 +414,21 @@ angular.module('JobSubmissionHelper', [ 'RestServices', 'Utilities', 'Credential var group_name = params.group_name; var group_source = params.group_source; + if (scope.removeSubmitRefreshCompleted) { + scope.removeSubmitRefreshCompleted(); + } + scope.removeSubmitRefreshCompleted = scope.$on('SubmitRefreshCompleted', function(e) { + Wait('stop'); + Alert('Update Started', 'The request to start the inventory update process was submitted. Monitor progress of the update process ' + + 'by clicking the button.', 'alert-info'); + }); + if (scope.removeUpdateSubmitted) { scope.removeUpdateSubmitted(); } scope.removeUpdateSubmitted = scope.$on('UpdateSubmitted', function(e, action) { if (action == 'started') { - // Refresh the project list after update request submitted - Alert('Update Started', 'The request to start the inventory update process was submitted. Monitor progress of the update process ' + - 'by clicking the Refresh button.', 'alert-info'); - //var node = $('#inventory-node') - //var selected = $('#tree-view').jstree('get_selected'); - //scope['inventorySummaryGroup'] = null; - //selected.each(function(idx) { - // $('#tree-view').jstree('deselect_node', $(this)); - // }); - //$('#tree-view').jstree('select_node', node); - BuildTree({ - scope: scope, - inventory_id: scope['inventory_id'], - emit_on_select: 'NodeSelect', - target_id: 'search-tree-container', - refresh: false, - moveable: true - }); + scope.refreshGroups('SubmitRefreshComplete'); } }); diff --git a/awx/ui/static/js/lists/InventoryGroups.js b/awx/ui/static/js/lists/InventoryGroups.js index d86e6a6dd9..2b06eb7c42 100644 --- a/awx/ui/static/js/lists/InventoryGroups.js +++ b/awx/ui/static/js/lists/InventoryGroups.js @@ -75,14 +75,11 @@ angular.module('InventoryGroupsDefinition', []) create: { mode: 'all', ngClick: "createGroup()", - ngHide: "groupCreateHide", - ngDisabled: 'grpBtnDisabled', + ngHide: 'selected_tree_id == 1', //disable when 'All Hosts' selected awToolTip: "Create a new group" }, properties: { mode: 'all', - ngHide: "groupEditHide", - ngDisabled: 'grpBtnDisabled', awToolTip: "Edit inventory properties" }, refresh: { @@ -96,7 +93,7 @@ angular.module('InventoryGroupsDefinition', []) mode: 'all', ngShow: "user_is_superuser" }, - help: { + help: { mode: 'all', awToolTip: //"

" + @@ -126,22 +123,25 @@ angular.module('InventoryGroupsDefinition', []) }, group_update: { //label: 'Sync', + mode: 'all', ngClick: 'updateGroup(\{\{ group.id \}\})', awToolTip: "\{\{ group.launch_tooltip \}\}", - ngShow: "group.id > 1", // hide for all hosts + ngShow: "group.id > 1 && (group.status !== 'running' && group.status !== 'pending' && group.status !== 'updating')", ngClass: "group.launch_class", dataPlacement: "top" }, cancel: { //label: 'Cancel', - ngClick: "cancelUpdate(\{\{ group.id \}\}, '\{\{ group.name \}\}')", + mode: 'all', + ngClick: "cancelUpdate(\{\{ group.id \}\})", awToolTip: "Cancel sync process", - ngClass: "group.cancel_class", - ngShow: "group.id > 1 && (group.status == 'running' || group.status == 'pending')", + 'class': 'red-txt', + ngShow: "group.id > 1 && (group.status == 'running' || group.status == 'pending' || group.status == 'updating')", dataPlacement: "top" }, edit: { //label: 'Edit', + mode: 'all', ngClick: "editGroup(\{\{ group.group_id \}\})", awToolTip: 'Edit group', ngShow: "group.id > 1", // hide for all hosts @@ -149,6 +149,7 @@ angular.module('InventoryGroupsDefinition', []) }, "delete": { //label: 'Delete', + mode: 'all', ngClick: "deleteGroup(\{\{ group.id \}\})", awToolTip: 'Delete group', ngShow: "group.id != 1", // hide for all hosts diff --git a/awx/ui/static/less/ansible-ui.less b/awx/ui/static/less/ansible-ui.less index 1e12f4ea1b..116765d940 100644 --- a/awx/ui/static/less/ansible-ui.less +++ b/awx/ui/static/less/ansible-ui.less @@ -55,6 +55,7 @@ body.modal-open { .no-bullets { list-style: none; } .capitalize { text-transform: capitalize; } .grey-txt { color: @grey; } +.red-txt { color: @red; } a.red-txt:hover { color: @red; } //make red links (for things like cancel) .text-center { text-align: center !important; } @@ -1040,6 +1041,8 @@ input[type="checkbox"].checkbox-no-label { .active-row { background-color: @white; + border-bottom: 1px solid #ddd; + border-right: 1px solid #ddd; } .node-toggle { diff --git a/awx/ui/static/lib/ansible/InventoryTree.js b/awx/ui/static/lib/ansible/InventoryTree.js index a8b798a216..9a07418779 100644 --- a/awx/ui/static/lib/ansible/InventoryTree.js +++ b/awx/ui/static/lib/ansible/InventoryTree.js @@ -178,6 +178,7 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper']) var inventory_id = params.inventory_id; var scope = params.scope; var refresh = params.refresh; + var emit = params.emit; //var selected_id = params. @@ -249,13 +250,13 @@ angular.module('InventoryTree', ['Utilities', 'RestServices', 'GroupsHelper']) Rest.get() .success( function(data, status, headers, config) { buildGroups(data, 0, 0); - console.log(groups); + //console.log(groups); if (refresh) { scope.groups = groups; - scope.$emit('groupTreeRefreshed'); + scope.$emit('groupTreeRefreshed', inventory_name, groups, emit); } else { - scope.$emit('groupTreeLoaded', inventory_name, groups); + scope.$emit('groupTreeLoaded', inventory_name, groups, emit); } }) .error( function(data, status, headers, config) { diff --git a/awx/ui/static/lib/ansible/Utilities.js b/awx/ui/static/lib/ansible/Utilities.js index 7a69eb0085..0a8c8d91b7 100644 --- a/awx/ui/static/lib/ansible/Utilities.js +++ b/awx/ui/static/lib/ansible/Utilities.js @@ -463,8 +463,37 @@ angular.module('Utilities',['RestServices', 'Utilities']) }); } }]) + + /* + * Search an array of objects, returning the matchting object or null + * + * Find({ list: [], key: "key", val: }); + */ + .factory('Find', [ function(){ + return function(params) { + var list = params.list; + var key = params.key; + var val = params.val; + var found = false; + if (typeof list == 'object' && Array.isArray(list)) { + for (var i=0; i < params.list.length; i++) { + if (list[i][key] == val) { + found = true; + break; + } + } + return (found) ? list[i] : null; + } + else { + // list parameter is not an array + return null; + } + + } + }]) - /* DeugForm({ form:
, scope: }); + /* + * DeugForm({ form: , scope: }); * * Use to log the $pristine and $valid properties of each form element. Helpful when form * buttons fail to enable/disable properly. diff --git a/awx/ui/static/lib/ansible/list-generator.js b/awx/ui/static/lib/ansible/list-generator.js index a87f9321d2..5b81707664 100644 --- a/awx/ui/static/lib/ansible/list-generator.js +++ b/awx/ui/static/lib/ansible/list-generator.js @@ -346,6 +346,7 @@ angular.module('ListGenerator', ['GeneratorHelpers']) var fAction = list.fieldActions[action]; html += "