Reload route after performing actions

This removes any special handler functions being used by some tables
and detail pages to refresh the data after performing an action and
just reloads the angular route instead. It's simpler and consistent.

Partially-Implements: blueprint horizon-lbaas-v2-ui
Change-Id: I6b696f51cf709e11bd027f8a10038874479c51db
This commit is contained in:
Justin Pomeroy 2016-03-07 21:24:11 -06:00
parent e208757a24
commit 1240c01a84
23 changed files with 99 additions and 111 deletions

View File

@ -51,7 +51,7 @@
function tableBatchActions( function tableBatchActions(
$q, $location, workflowModal, policy, gettext, loadBalancersService, deleteService $q, $location, workflowModal, policy, gettext, loadBalancersService, deleteService
) { ) {
var loadBalancerIsActionable, loadBalancerId, handler; var loadBalancerIsActionable, loadBalancerId;
var create = workflowModal.init({ var create = workflowModal.init({
controller: 'CreateListenerWizardController', controller: 'CreateListenerWizardController',
@ -69,9 +69,8 @@
/////////////// ///////////////
function init(_loadBalancerId_, _handler_) { function init(_loadBalancerId_) {
loadBalancerId = _loadBalancerId_; loadBalancerId = _loadBalancerId_;
handler = _handler_;
loadBalancerIsActionable = loadBalancersService.isActionable(loadBalancerId); loadBalancerIsActionable = loadBalancersService.isActionable(loadBalancerId);
return service; return service;
} }
@ -84,7 +83,7 @@
text: gettext('Create Listener') text: gettext('Create Listener')
} }
},{ },{
service: deleteService.init(loadBalancerId, loadBalancerIsActionable, handler), service: deleteService.init(loadBalancerId, loadBalancerIsActionable),
template: { template: {
text: gettext('Delete Listeners'), text: gettext('Delete Listeners'),
type: 'delete-selected' type: 'delete-selected'

View File

@ -54,7 +54,7 @@
function deleteService( function deleteService(
$q, $location, $route, deleteModal, api, policy, toast, qExtensions, gettext $q, $location, $route, deleteModal, api, policy, toast, qExtensions, gettext
) { ) {
var loadbalancerId, statePromise, handler; var loadbalancerId, statePromise;
var context = { var context = {
labels: { labels: {
title: gettext('Confirm Delete Listeners'), title: gettext('Confirm Delete Listeners'),
@ -80,10 +80,9 @@
////////////// //////////////
function init(_loadbalancerId_, _statePromise_, _handler_) { function init(_loadbalancerId_, _statePromise_) {
loadbalancerId = _loadbalancerId_; loadbalancerId = _loadbalancerId_;
statePromise = _statePromise_; statePromise = _statePromise_;
handler = _handler_;
return service; return service;
} }
@ -108,9 +107,7 @@
} }
function actionComplete(eventType) { function actionComplete(eventType) {
if (angular.isFunction(handler)) { if (eventType === context.failedEvent) {
handler();
} else if (eventType === context.failedEvent) {
// Action failed, reload the page // Action failed, reload the page
$route.reload(); $route.reload();
} else { } else {

View File

@ -157,14 +157,5 @@
expect(toast.add).toHaveBeenCalledWith('success', 'Deleted listeners: First.'); expect(toast.add).toHaveBeenCalledWith('success', 'Deleted listeners: First.');
}); });
it('should call handler function if provided', function() {
var handler = { handler: angular.noop };
spyOn(handler, 'handler');
service.init('1', null, handler.handler);
service.perform(items);
$scope.$apply();
expect(handler.handler).toHaveBeenCalled();
});
}); });
})(); })();

View File

@ -54,7 +54,7 @@
$q, $route, workflowModal, policy, gettext, loadBalancersService, deleteService, $q, $route, workflowModal, policy, gettext, loadBalancersService, deleteService,
createPoolService createPoolService
) { ) {
var loadbalancerId, loadBalancerIsActionable, handler; var loadbalancerId, loadBalancerIsActionable;
var edit = workflowModal.init({ var edit = workflowModal.init({
controller: 'EditListenerWizardController', controller: 'EditListenerWizardController',
@ -72,9 +72,8 @@
/////////////// ///////////////
function init(_loadbalancerId_, _handler_) { function init(_loadbalancerId_) {
loadbalancerId = _loadbalancerId_; loadbalancerId = _loadbalancerId_;
handler = _handler_;
loadBalancerIsActionable = loadBalancersService.isActionable(loadbalancerId); loadBalancerIsActionable = loadBalancersService.isActionable(loadbalancerId);
return service; return service;
} }
@ -91,7 +90,7 @@
text: gettext('Create Pool') text: gettext('Create Pool')
} }
},{ },{
service: deleteService.init(loadbalancerId, loadBalancerIsActionable, handler), service: deleteService.init(loadbalancerId, loadBalancerIsActionable),
template: { template: {
text: gettext('Delete Listener'), text: gettext('Delete Listener'),
type: 'delete' type: 'delete'

View File

@ -48,8 +48,8 @@
ctrl.src = []; ctrl.src = [];
ctrl.checked = {}; ctrl.checked = {};
ctrl.loadbalancerId = $routeParams.loadbalancerId; ctrl.loadbalancerId = $routeParams.loadbalancerId;
ctrl.batchActions = batchActions.init(ctrl.loadbalancerId, init); ctrl.batchActions = batchActions.init(ctrl.loadbalancerId);
ctrl.rowActions = rowActions.init(ctrl.loadbalancerId, init); ctrl.rowActions = rowActions.init(ctrl.loadbalancerId);
init(); init();

View File

@ -66,7 +66,7 @@
expect(ctrl.src).toEqual(items); expect(ctrl.src).toEqual(items);
expect(ctrl.checked).toEqual({}); expect(ctrl.checked).toEqual({});
expect(ctrl.loadbalancerId).toEqual('1234'); expect(ctrl.loadbalancerId).toEqual('1234');
expect(rowActions.init).toHaveBeenCalledWith(ctrl.loadbalancerId, jasmine.any(Function)); expect(rowActions.init).toHaveBeenCalledWith(ctrl.loadbalancerId);
expect(ctrl.rowActions).toBeDefined(); expect(ctrl.rowActions).toBeDefined();
expect(ctrl.rowActions).toEqual(rowActions); expect(ctrl.rowActions).toEqual(rowActions);
expect(ctrl.batchActions).toBeDefined(); expect(ctrl.batchActions).toBeDefined();

View File

@ -47,7 +47,6 @@
*/ */
function tableBatchActions($location, workflowModal, basePath, deleteService, policy, gettext) { function tableBatchActions($location, workflowModal, basePath, deleteService, policy, gettext) {
var handler;
var create = workflowModal.init({ var create = workflowModal.init({
controller: 'CreateLoadBalancerWizardController', controller: 'CreateLoadBalancerWizardController',
@ -57,19 +56,13 @@
}); });
var service = { var service = {
actions: actions, actions: actions
init: init
}; };
return service; return service;
/////////////// ///////////////
function init(_handler_) {
handler = _handler_;
return service;
}
function actions() { function actions() {
return [{ return [{
service: create, service: create,
@ -78,7 +71,7 @@
text: gettext('Create Load Balancer') text: gettext('Create Load Balancer')
} }
}, { }, {
service: deleteService.init(handler), service: deleteService,
template: { template: {
type: 'delete-selected', type: 'delete-selected',
text: gettext('Delete Load Balancers') text: gettext('Delete Load Balancers')

View File

@ -54,8 +54,6 @@
function deleteService( function deleteService(
$q, $location, $route, deleteModal, api, policy, toast, qExtensions, gettext $q, $location, $route, deleteModal, api, policy, toast, qExtensions, gettext
) { ) {
var handler;
// If a batch delete, then this message is displayed for any selected load balancers not in // If a batch delete, then this message is displayed for any selected load balancers not in
// ACTIVE or ERROR state. // ACTIVE or ERROR state.
var notAllowedMessage = gettext('The following load balancers are pending and cannot be ' + var notAllowedMessage = gettext('The following load balancers are pending and cannot be ' +
@ -77,19 +75,13 @@
var service = { var service = {
perform: perform, perform: perform,
allowed: allowed, allowed: allowed
init: init
}; };
return service; return service;
////////////// //////////////
function init(_handler_) {
handler = _handler_;
return service;
}
function perform(items) { function perform(items) {
if (angular.isArray(items)) { if (angular.isArray(items)) {
qExtensions.allSettled(items.map(checkPermission)).then(afterCheck); qExtensions.allSettled(items.map(checkPermission)).then(afterCheck);
@ -144,9 +136,7 @@
} }
function actionComplete(eventType) { function actionComplete(eventType) {
if (angular.isFunction(handler)) { if (eventType === context.failedEvent) {
handler();
} else if (eventType === context.failedEvent) {
// Action failed, reload the page // Action failed, reload the page
$route.reload(); $route.reload();
} else { } else {

View File

@ -173,14 +173,5 @@
expect(toast.add).toHaveBeenCalledWith('success', 'Deleted load balancers: First.'); expect(toast.add).toHaveBeenCalledWith('success', 'Deleted load balancers: First.');
}); });
it('should call handler function if provided', function() {
var handler = { handler: angular.noop };
spyOn(handler, 'handler');
service.init(handler.handler);
service.perform(items);
$scope.$apply();
expect(handler.handler).toHaveBeenCalled();
});
}); });
})(); })();

View File

@ -66,8 +66,6 @@
qExtensions, qExtensions,
gettext gettext
) { ) {
var handler;
var edit = workflowModal.init({ var edit = workflowModal.init({
controller: 'EditLoadBalancerWizardController', controller: 'EditLoadBalancerWizardController',
message: gettext('The load balancer has been updated.'), message: gettext('The load balancer has been updated.'),
@ -76,19 +74,13 @@
}); });
var service = { var service = {
actions: actions, actions: actions
init: init
}; };
return service; return service;
/////////////// ///////////////
function init(_handler_) {
handler = _handler_;
return service;
}
function actions() { function actions() {
return [{ return [{
service: edit, service: edit,
@ -106,7 +98,7 @@
text: gettext('Disassociate Floating IP') text: gettext('Disassociate Floating IP')
} }
},{ },{
service: deleteService.init(handler), service: deleteService,
template: { template: {
text: gettext('Delete Load Balancer'), text: gettext('Delete Load Balancer'),
type: 'delete' type: 'delete'

View File

@ -24,7 +24,9 @@
'horizon.app.core.openstack-service-api.lbaasv2', 'horizon.app.core.openstack-service-api.lbaasv2',
'horizon.dashboard.project.lbaasv2.loadbalancers.actions.rowActions', 'horizon.dashboard.project.lbaasv2.loadbalancers.actions.rowActions',
'horizon.dashboard.project.lbaasv2.loadbalancers.service', 'horizon.dashboard.project.lbaasv2.loadbalancers.service',
'$routeParams' '$routeParams',
'$window',
'$scope'
]; ];
/** /**
@ -38,15 +40,20 @@
* @param rowActions The load balancer row actions service. * @param rowActions The load balancer row actions service.
* @param loadBalancersService The LBaaS v2 load balancers service. * @param loadBalancersService The LBaaS v2 load balancers service.
* @param $routeParams The angular $routeParams service. * @param $routeParams The angular $routeParams service.
* @param $window Angular's reference to the browser window object.
* @param $scope The angular scope object.
* @returns undefined * @returns undefined
*/ */
function LoadBalancerDetailController(api, rowActions, loadBalancersService, $routeParams) { function LoadBalancerDetailController(
api, rowActions, loadBalancersService, $routeParams, $window, $scope
) {
var ctrl = this; var ctrl = this;
ctrl.actions = rowActions.actions; ctrl.actions = rowActions.actions;
ctrl.operatingStatus = loadBalancersService.operatingStatus; ctrl.operatingStatus = loadBalancersService.operatingStatus;
ctrl.provisioningStatus = loadBalancersService.provisioningStatus; ctrl.provisioningStatus = loadBalancersService.provisioningStatus;
ctrl.listenersTabActive = $window.listenersTabActive;
init(); init();
@ -60,6 +67,14 @@
ctrl.loadbalancer = response; ctrl.loadbalancer = response;
} }
// Save the active state of the listeners tab in the global window object so it can stay
// active after reloading the route following an action.
$scope.$watch(function() {
return ctrl.listenersTabActive;
}, function(active) {
$window.listenersTabActive = active;
});
} }
})(); })();

View File

@ -17,12 +17,12 @@
'use strict'; 'use strict';
describe('LBaaS v2 Load Balancer Detail Controller', function() { describe('LBaaS v2 Load Balancer Detail Controller', function() {
var controller, lbaasv2API, loadbalancer; var lbaasv2API, ctrl, $scope, $window;
function fakeAPI() { function fakeAPI() {
return { return {
success: function(callback) { success: function(callback) {
callback(loadbalancer); callback({ id: '1234' });
} }
}; };
} }
@ -40,24 +40,33 @@
})); }));
beforeEach(inject(function($injector) { beforeEach(inject(function($injector) {
loadbalancer = { id: '1234' };
lbaasv2API = $injector.get('horizon.app.core.openstack-service-api.lbaasv2'); lbaasv2API = $injector.get('horizon.app.core.openstack-service-api.lbaasv2');
controller = $injector.get('$controller');
spyOn(lbaasv2API, 'getLoadBalancer').and.callFake(fakeAPI); spyOn(lbaasv2API, 'getLoadBalancer').and.callFake(fakeAPI);
})); $scope = $injector.get('$rootScope').$new();
$window = {};
function createController() { var controller = $injector.get('$controller');
return controller('LoadBalancerDetailController', { ctrl = controller('LoadBalancerDetailController', {
api: lbaasv2API, $scope: $scope,
$window: $window,
$routeParams: { loadbalancerId: '1234' } $routeParams: { loadbalancerId: '1234' }
}); });
} }));
it('should invoke lbaasv2 apis', function() { it('should invoke lbaasv2 apis', function() {
createController();
expect(lbaasv2API.getLoadBalancer).toHaveBeenCalledWith('1234', true); expect(lbaasv2API.getLoadBalancer).toHaveBeenCalledWith('1234', true);
}); });
it('should save changes to listeners tab active state', function() {
expect($window.listenersTabActive).toBeUndefined();
expect(ctrl.listenersTabActive).toBeUndefined();
ctrl.listenersTabActive = true;
$scope.$apply();
expect($window.listenersTabActive).toBe(true);
ctrl.listenersTabActive = false;
$scope.$apply();
expect($window.listenersTabActive).toBe(false);
});
}); });
})(); })();

View File

@ -47,7 +47,7 @@
</div> </div>
</div> </div>
</tab> </tab>
<tab heading="{$ 'Listeners' | translate $}"> <tab heading="{$ 'Listeners' | translate $}" active="ctrl.listenersTabActive">
<ng-include src="'static/dashboard/project/lbaasv2/listeners/table.html'"></ng-include> <ng-include src="'static/dashboard/project/lbaasv2/listeners/table.html'"></ng-include>
</tab> </tab>
</tabset> </tabset>

View File

@ -47,8 +47,8 @@
ctrl.items = []; ctrl.items = [];
ctrl.src = []; ctrl.src = [];
ctrl.checked = {}; ctrl.checked = {};
ctrl.batchActions = batchActions.init(init); ctrl.batchActions = batchActions;
ctrl.rowActions = rowActions.init(init); ctrl.rowActions = rowActions;
ctrl.operatingStatus = loadBalancersService.operatingStatus; ctrl.operatingStatus = loadBalancersService.operatingStatus;
ctrl.provisioningStatus = loadBalancersService.provisioningStatus; ctrl.provisioningStatus = loadBalancersService.provisioningStatus;

View File

@ -58,7 +58,7 @@
toastService, toastService,
gettext gettext
) { ) {
var poolId, statePromise, handler; var poolId, statePromise;
var service = { var service = {
perform: open, perform: open,
@ -70,10 +70,9 @@
//////////// ////////////
function init(_poolId_, _statePromise_, _handler_) { function init(_poolId_, _statePromise_) {
poolId = _poolId_; poolId = _poolId_;
statePromise = _statePromise_; statePromise = _statePromise_;
handler = _handler_;
return service; return service;
} }
@ -116,11 +115,7 @@
function onModalClose() { function onModalClose() {
toastService.add('success', gettext('Pool member weight has been updated.')); toastService.add('success', gettext('Pool member weight has been updated.'));
if (angular.isFunction(handler)) { $route.reload();
handler();
} else {
$route.reload();
}
} }
} }

View File

@ -105,15 +105,6 @@
expect($route.reload).toHaveBeenCalled(); expect($route.reload).toHaveBeenCalled();
}); });
it('should call handler function upon closing modal if provided', function() {
var handler = { handler: angular.noop };
spyOn(handler, 'handler');
service.init('1', fakePromise(), handler.handler);
service.perform(member);
$scope.$apply();
expect(handler.handler).toHaveBeenCalled();
});
}); });
})(); })();

View File

@ -40,7 +40,7 @@
*/ */
function rowActions(gettext, loadBalancersService, editWeight) { function rowActions(gettext, loadBalancersService, editWeight) {
var loadBalancerIsActionable, poolId, handler; var loadBalancerIsActionable, poolId;
var service = { var service = {
actions: actions, actions: actions,
@ -51,16 +51,15 @@
/////////////// ///////////////
function init(loadbalancerId, _poolId_, _handler_) { function init(loadbalancerId, _poolId_) {
loadBalancerIsActionable = loadBalancersService.isActionable(loadbalancerId); loadBalancerIsActionable = loadBalancersService.isActionable(loadbalancerId);
poolId = _poolId_; poolId = _poolId_;
handler = _handler_;
return service; return service;
} }
function actions() { function actions() {
return [{ return [{
service: editWeight.init(poolId, loadBalancerIsActionable, handler), service: editWeight.init(poolId, loadBalancerIsActionable),
template: { template: {
text: gettext('Update Weight') text: gettext('Update Weight')
} }

View File

@ -42,7 +42,7 @@
function MemberDetailController(api, rowActions, $routeParams) { function MemberDetailController(api, rowActions, $routeParams) {
var ctrl = this; var ctrl = this;
ctrl.actions = rowActions.init($routeParams.loadbalancerId, $routeParams.poolId, init).actions; ctrl.actions = rowActions.init($routeParams.loadbalancerId, $routeParams.poolId).actions;
init(); init();

View File

@ -90,7 +90,7 @@
it('should have actions', function() { it('should have actions', function() {
expect(ctrl.actions).toBe('member-actions'); expect(ctrl.actions).toBe('member-actions');
expect(actions.init).toHaveBeenCalledWith('loadbalancerId', 'poolId', jasmine.any(Function)); expect(actions.init).toHaveBeenCalledWith('loadbalancerId', 'poolId');
}); });
}); });

View File

@ -48,7 +48,7 @@
ctrl.loadbalancerId = $routeParams.loadbalancerId; ctrl.loadbalancerId = $routeParams.loadbalancerId;
ctrl.listenerId = $routeParams.listenerId; ctrl.listenerId = $routeParams.listenerId;
ctrl.poolId = $routeParams.poolId; ctrl.poolId = $routeParams.poolId;
ctrl.rowActions = rowActions.init(ctrl.loadbalancerId, ctrl.poolId, init); ctrl.rowActions = rowActions.init(ctrl.loadbalancerId, ctrl.poolId);
init(); init();

View File

@ -24,7 +24,9 @@
'horizon.app.core.openstack-service-api.lbaasv2', 'horizon.app.core.openstack-service-api.lbaasv2',
'horizon.dashboard.project.lbaasv2.pools.actions.rowActions', 'horizon.dashboard.project.lbaasv2.pools.actions.rowActions',
'$routeParams', '$routeParams',
'horizon.framework.util.i18n.gettext' 'horizon.framework.util.i18n.gettext',
'$window',
'$scope'
]; ];
/** /**
@ -38,10 +40,12 @@
* @param rowActions The LBaaS v2 pool row actions service. * @param rowActions The LBaaS v2 pool row actions service.
* @param $routeParams The angular $routeParams service. * @param $routeParams The angular $routeParams service.
* @param gettext The horizon gettext function for translation. * @param gettext The horizon gettext function for translation.
* @param $window Angular's reference to the browser window object.
* @param $scope The angular scope object.
* @returns undefined * @returns undefined
*/ */
function PoolDetailController(api, rowActions, $routeParams, gettext) { function PoolDetailController(api, rowActions, $routeParams, gettext, $window, $scope) {
var ctrl = this; var ctrl = this;
ctrl.loadBalancerAlgorithm = { ctrl.loadBalancerAlgorithm = {
@ -49,8 +53,8 @@
'LEAST_CONNECTIONS': gettext('Least Connections'), 'LEAST_CONNECTIONS': gettext('Least Connections'),
'SOURCE_IP': gettext('Source IP') 'SOURCE_IP': gettext('Source IP')
}; };
ctrl.actions = rowActions.init($routeParams.loadbalancerId, $routeParams.listenerId).actions; ctrl.actions = rowActions.init($routeParams.loadbalancerId, $routeParams.listenerId).actions;
ctrl.membersTabActive = $window.membersTabActive;
init(); init();
@ -68,6 +72,14 @@
}, property); }, property);
} }
// Save the active state of the members tab in the global window object so it can stay
// active after reloading the route following an action.
$scope.$watch(function() {
return ctrl.membersTabActive;
}, function(active) {
$window.membersTabActive = active;
});
} }
})(); })();

View File

@ -17,7 +17,7 @@
'use strict'; 'use strict';
describe('LBaaS v2 Pool Detail Controller', function() { describe('LBaaS v2 Pool Detail Controller', function() {
var lbaasv2API, ctrl; var lbaasv2API, ctrl, $scope, $window;
function fakeAPI() { function fakeAPI() {
return { return {
@ -52,8 +52,12 @@
spyOn(lbaasv2API, 'getPool').and.callFake(fakeAPI); spyOn(lbaasv2API, 'getPool').and.callFake(fakeAPI);
spyOn(lbaasv2API, 'getListener').and.callFake(fakeAPI); spyOn(lbaasv2API, 'getListener').and.callFake(fakeAPI);
spyOn(lbaasv2API, 'getLoadBalancer').and.callFake(loadbalancerAPI); spyOn(lbaasv2API, 'getLoadBalancer').and.callFake(loadbalancerAPI);
$scope = $injector.get('$rootScope').$new();
$window = {};
var controller = $injector.get('$controller'); var controller = $injector.get('$controller');
ctrl = controller('PoolDetailController', { ctrl = controller('PoolDetailController', {
$scope: $scope,
$window: $window,
$routeParams: { $routeParams: {
loadbalancerId: 'loadbalancerId', loadbalancerId: 'loadbalancerId',
listenerId: 'listenerId', listenerId: 'listenerId',
@ -75,6 +79,17 @@
expect(ctrl.loadBalancerAlgorithm).toBeDefined(); expect(ctrl.loadBalancerAlgorithm).toBeDefined();
}); });
it('should save changes to members tab active state', function() {
expect($window.membersTabActive).toBeUndefined();
expect(ctrl.membersTabActive).toBeUndefined();
ctrl.membersTabActive = true;
$scope.$apply();
expect($window.membersTabActive).toBe(true);
ctrl.membersTabActive = false;
$scope.$apply();
expect($window.membersTabActive).toBe(false);
});
}); });
})(); })();

View File

@ -40,7 +40,7 @@
</div> </div>
</div> </div>
</tab> </tab>
<tab heading="{$ 'Members' | translate $}"> <tab heading="{$ 'Members' | translate $}" active="ctrl.membersTabActive">
<ng-include src="'static/dashboard/project/lbaasv2/members/table.html'"></ng-include> <ng-include src="'static/dashboard/project/lbaasv2/members/table.html'"></ng-include>
</tab> </tab>
</tabset> </tabset>