Application Packaging Standard

Last updated 18-Mar-2019

Resource List

Continue the development of the presentation level.

../../../../../_images/generic-step-model1.png ../../../../../_images/generic-step-meta1.png ../../../../../_images/generic-step-provision1.png ../../../../../_images/generic-step-presentation-b.png ../../../../../_images/generic-step-deploy1.png ../../../../../_images/generic-step-provisioning1.png

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.

../../../../../_images/generic-presentation-step-aux.png ../../../../../_images/generic-presentation-step-add.png ../../../../../_images/generic-presentation-step-list-b.png ../../../../../_images/generic-presentation-step-edit.png

Grid Design

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.

../../../../../_images/nextcp-layout.png

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 - call the custom start operation
    • Stop - call the custom stop operation
    • Delete - remove the selected resources
    • Refresh - update the grid

    In each button definition, specify the button handler by setting the onClick property.

Button Handlers

For each button inside the grid toolbar, define a handler as explained in the demo.

onContext Method

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.

onHide Method

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.

Continue Your Demo Project

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.

  1. 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. */
          }
       });
    });
    
  2. 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 [
    
       ];
    },
    
  3. 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(); }
                }
             );
          });
      };
      
  4. 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
                   }]
             ]]
       ]];
    
  5. Define the onContext method:

    onContext: function() {
        this.grid = this.byId("srv_grid");
        this.page = this.byId("apsPageContainer");
        this.grid.refresh();
        aps.apsc.hideLoading();
    },
    
  6. 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.