Continue the development of the presentation level.
Develop the view with a grid that shows a list of provisioned resources (VPSes) with their properties. The view must allow a customer to select resources and perform some operations, such as start/stop or delete. It must also allow a customer to add a new VPS or open a VPS in the editor.
In this document:
To present a list of resources, a typical view contains a grid including a toolbar and a table. The toolbar allows a customer to start operations over a single or multiple selected resources.
Follow these recommendations when designing a grid:
In the Single Page Application technique, the init
method must return a grid definition.
To find the grid in a function, assign the widget ID to the grid. The best way is to use the genId method
for it to ensure the ID uniqueness, for example, id:this.genId("srv-grid")
.
The store
property must refer to a resource store providing a list of resources
to present in the grid.
To allow multiple selection, set the grid selection mode - selectionMode:"multiple"
.
In the columns
property, define an array of columns to present the resource properties.
One of the columns must call the resource editor when a user clicks on it. In this scenario, the editor
is the server-edit
view, and the clickable column is the one that presents the resource name
property.
So, this is implemented by setting the following two properties:
Assign the view ID to the grid apsResourceViewId
property, that is apsResourceViewId:"server-edit"
.
Set the column type as follows - type:""resourceName"
.
Define the toolbar containing definition of the following buttons:
New - start creating a new resource
Start - for simplicity, only change the state of the selected VPSes to “Running” (but typically, must call a custom operation to start those VPSes)
Stop - for simplicity, only change the state of the selected VPSes to “Stopped” (but typically, must call a custom operation to stop those VPSes)
Delete - remove the selected resources
Refresh - update the grid
In each button definition, specify the button handler by setting the onClick
property.
For each button inside the grid toolbar, define a handler as explained in the demo.
UX1 calls the onContext
method after it loads the widgets and adds the widget IDs to the widget registry.
The method must find the grid and its parent apsPageContainer
container
and then refresh the grid following these rules:
The default ID of the parent APS/PageContainer is apsPageContainer
as specified
in bootstrapApp.html.
Find the grid ID using the byId method.
Once the grid ID is discovered, run its internal refresh
method to display the latest list of the resources
and their properties.
This keeps the grid up to date each time the onContext
method is called.
Finally, after all operations are completed, call the aps.apsc.hideLoading()
method to hide
the Loading state of the view.
After a user clicks on the New button, the latter will stay in the busy status even though
the user will be redirected to the server-new
view and then returned back.
UX1 calls the onHide
method when a user is leaving the view. Use this method to clean
the status of the New button.
This section continues the demo project from its previous step.
The ui/servers.js
file presents a list of VPSes in a subscription.
Follow these steps, when creating the file from scratch.
Create the skeleton of the custom JavaScript:
define([
"dojo/_base/declare",
"dojo/when",
"aps/View",
"aps/ResourceStore",
"./displayError"
], function (declare, when, View, Store, displayError) {
var self; // Global variable to exchange data between methods
return declare(View, {
init: function() {
self = this; // Refer to the declared View object
/* Initialize data only the first time this view is called
after the base page is loaded or re-loaded.
It is missed in all subsequent cases when the view is called. */
},
onContext: function() {
/* Performs some actions before visualizing widgets.
On completion of this method and before the onContext method is completed,
widgets are visible but not available for operations with them */
},
onHide: function() {
/* Performs some actions on leaving the view for another view. */
}
});
});
Define the structure of the init
function:
init: function() {
self = this; // Refer to the declared View object
/* Define the data store */
var vpsStore = new Store({
apsType: "http://aps-standard.org/samples/vpsdemo/vps/1.0",
target: "/aps/2/resources/"
});
/* Define widget processing logic */
/* Define and return widgets */
return [
];
},
Define widget processing logic in the init
method.
New button handler:
var add = function() {
/* Start the process of creating a VPS by going to the first screen */
aps.apsc.gotoView("server-new-1");
};
Refresh button handler:
var refresh = function() {
self.grid.refresh();
};
The content of the grid widget is filled out with actual data in the onContext
method
by calling the refresh
function.
A change state function is needed for stopping and starting the selected VPSes:
function changeState(state, btn) {
/* Get the list of selected VPSes */
var sel = self.grid.get("selectionArray"),
counter = sel.length;
/* Clear the current messages on the screen */
self.page.get("messageList").removeAll(); // The page widget is defined by the ``onContext`` method
sel.forEach(function(vpsId){
console.log("I'm trying to change state of VPS with id = [" + vpsId + "]");
var vps = {
aps: { id: vpsId },
state: state
};
/* Save the VPS properties */
when(vpsStore.put(vps),
/* If success, process the next VPS until the list is empty */
function() {
console.log("State of VPS with id = [" + vpsId + "] changed");
/* Remove the processed VPS from the selection */
sel.splice(sel.indexOf(vpsId),1);
self.grid.refresh();
/* Remove busy state of the button */
if (--counter === 0) { btn.cancel(); }
},
/* If failure, call the error handler */
function(e) {
displayError(e);
if (--counter === 0) { btn.cancel(); }
}
);
});
}
Stop button handler:
var stop = function() {
changeState("Stopped", this);
};
Start button handler:
var start = function() {
changeState("Running", this);
};
Delete button handler:
var remove = function() {
var btn = this;
/* Get confirmation from the user for the delete operation */
if (!confirm("Are you sure you want delete VPSes?")) {
btn.cancel();
return;
}
var sel = self.grid.get("selectionArray");
var counter = sel.length;
/* Clear the current messages on the screen */
self.page.get("messageList").removeAll();
sel.forEach(function(vpsId){
console.log("I'm trying to delete VPS with id = [" + vpsId + "]");
/* Remove the VPS from the APS controller DB */
when(vpsStore.remove(vpsId),
/* If success, process the next VPS until the list is empty */
function(){
console.log("VPS with id = [" + vpsId + "] removed");
/* Remove the processed VPS from the selection */
sel.splice(sel.indexOf(vpsId),1);
self.grid.refresh();
if (--counter === 0) { btn.cancel(); }
},
/* If failure, call the error handler */
function(e){
displayError(e);
if (--counter === 0) { btn.cancel(); }
}
);
});
};
Define and return widgets in the init
function:
return ["aps/Grid", {
id: this.genId("srv_grid"),
store: store,
selectionMode: "multiple",
apsResourceViewId: "server-edit",
noDataText: "No servers provisioned",
noEntriesFoundText: "No servers meet your search criteria",
columns: [{
field: "name",
name: "Name",
type: "resourceName",
filter: true
}, {
field: "hardware.memory",
name: "RAM",
title: "RAM",
type: "integer",
filter: true
}, {
field: "hardware.diskspace",
name: "Disk Space",
title: "Disk",
type: "integer",
filter: true
}, {
field: "hardware.CPU.number",
name: "CPU",
type: "integer",
filter: true
}, {
field: "platform.OS.name",
name: "OS",
filter: true
}, {
field: "state",
name: "State",
filter: true
}]}, [
["aps/Toolbar", [
["aps/ToolbarButton", {
id: this.genId("srv_new"),
iconClass:"fa-plus",
type: "primary",
label: "New",
onClick: add
}],
["aps/ToolbarButton", {
id: this.genId("srv_start"),
iconClass:"fa-play",
type: "success",
label: "Start",
requireItems: true,
onClick: start
}],
["aps/ToolbarButton", {
id: this.genId("srv_stop"),
iconClass:"fa-pause",
type: "warning",
label: "Stop",
requireItems: true,
onClick: stop
}],
["aps/ToolbarButton", {
id: this.genId("srv_delete"),
iconClass:"fa-trash",
type: "danger",
label: "Delete",
requireItems: true,
onClick: remove
}],
["aps/ToolbarButton", {
id: this.genId("srv_refresh"),
iconClass:"fa-refresh",
label: "Refresh",
autoBusy: false,
onClick: refresh
}]
]]
]];
Define the onContext
method:
onContext: function() {
this.grid = this.byId("srv_grid");
this.page = this.byId("apsPageContainer");
this.grid.refresh();
aps.apsc.hideLoading();
},
Define the onHide
function.
onHide: function() {
this.byId("srv_new").cancel();
}
This completes the development of the servers
view.
Note
The project files you have created are similar to the respective files in the
sample package
.