HTML Client SDK Javascript API

  1. Overview
  2. Object Views and Data Access
  3. Actions and Modal Dialogs
  4. Global Views
  5. Other APIs
  6. Internationalization
  7. API Summary

1. Overview

This page documents the Javascript API of HTML Client SDK 6.5, which is identical to the HTML Bridge released since Web Client SDK 5.5.1. This API allows to build HTML plugins that are compatible with both the HTML Client and Flex Client. Please refer to the Plug-in Architecture Diagram in Getting Started with HTML Client SDK or in the on-line SDK 6.5 documentation.

API initialization (web-platform.js)

The API is initialized through a custom file web-platform.js that must be included in each HTML resources as shown in the SDK samples. This file defines the Javascript global variable WEB_PLATFORM which is the entry point to most APIs. Plugin projects generated by the scripts included in /Tools already contain web-platform.js with the correct plugin and package names.

Do not modify the content of web-platform.js, use separate JS file to implement your own methods.

Limitations of HTML content for plugins running in the Flex Client

Since Adobe Flex doesn't support HTML content natively we use an iFrame to display HTML views over the Flex container. Only one HTML view can be opened at a time. In particular portlets with HTML content are not supported directly but there is a work-around described below.

If a Flex menu or dialog overlaps an HTML view, the HTML view is removed temporarily because otherwise it would remain in front of the Flex component and cut if off (the iFrame being in a different plane than the the Flash container). It is also removed when resizing the view with the vertical separators on the left or the right-hand size in order to make the resizing smoother.

2. Object Views and Data Access

Extension points for HTML views are the same as for Flex views but use the generic bridge class com.vmware.vsphere.client.htmlbridge.HtmlView which takes care of displaying your html content inside an iFrame. The only time users will perceive a difference between that view and the rest of the client is when the iFrame disappears temporarily, during the resizing of LHS or RHS splitter bars, or if a Flex menu or dialog pops up and overlaps the view.

Manage and Monitor views

To extend an object view under the Manage or Monitor tab use the generic HtmlView as the Flex componentClass and set its url to the view's html source, like this:

   <!-- Adding a VM Monitor tab -->
   <extension id="com.vmware.samples.vspherewssdk.vm.monitor">
      <extendedPoint>vsphere.core.vm.monitorViews</extendedPoint>
      <object>
         <name>#{monitorTab.label}</name>
         <componentClass className="com.vmware.vsphere.client.htmlbridge.HtmlView">
            <object>
               <root>
                  <url>/vsphere-client/vspherewssdk/resources/vm-monitor.html</url>
               </root>
            </object>
         </componentClass>
      </object>
   </extension>

The <url> value is a relative URL starting with your plugin web context path, here /vsphere-client/vspherewssdk, which is also the Web-ContextPath defined in MANIFEST.MF (except for the starting /).
resources/vm-monitor.html is the relative path of the html file under your project's webapp folder.

The relative URL path must end with .html in order for the view's http session to be authenticated properly.

https URLs can also be used to display content from another domain in that view, but the content won't load the first time the user goes there, unless the certificate was already verified. http URLs won't work, modern browsers blocks insecure content inside the secure Web Client domain.

Notice that the view name "WSSDK sample" is localized like all labels in plugin.xml by using the syntax #{monitorTab.label} where monitorTab.label is the resource key in the plugin's properties file.

Sample running in Flex client 6.0

The view document vm-monitor.html is opened with a request containing the following parameters:

objectId is the most useful of the parameters passed to the view. This is the id to use to build the data URL for fetching object properties (see buildDataUrl API).

Note that when the HtmlView is first created the objectId parameter may be null, so your view code must check for a null objectId before it runs.

objectType is only provided for vSphere objects in the case of the HTML Client, i.e. VirtualMachine, HostSystem, etc.

Image of the view content generated when a Flex component overlaps

If a menu or dialog overlaps an HTML view then an image of that view is automatically generated in order to keep the content visible behind the Flex component. Otherwise the view would blank out because the iFrame holding the HTML content is hidden temporarily to avoid cutting off the Flex component. This technique is not perfect (the image is not always accurate, the font may change, etc.) so users may notice some variations. It is only temporary as the live content is restored once the menu or dialog disappear from the front.

The image generation is turned on by default for all views. You can turn it off view by view by setting the flag useHtmlContentImage to false as shown below.

   <componentClass className="com.vmware.vsphere.client.htmlbridge.HtmlView">
      <object>
         <root>
            <url>/vsphere-client/vspherewssdk/resources/vm-monitor.html</url>
            <useHtmlContentImage>false</useHtmlContentImage>
         </root>
      </object>
   </componentClass>

vSphere Object Portlets (summarySectionViews)

To extend an object's Summary view, use the generic HtmlView componentClass as shown below:

   <!-- VM summary sample view -->
   <extension id="com.vmware.samples.vspherewssdk.vm.summary">
      <extendedPoint>vsphere.core.vm.summarySectionViews</extendedPoint>
      <object>
         <name>#{summaryView.title}</name>
         <componentClass className="com.vmware.vsphere.client.htmlbridge.HtmlView">
            <object>
               <root>
                  <url>/vsphere-client/vspherewssdk/resources/vm-summary.html</url>
                  <dialogTitle>WSSDK Summary Sample</dialogTitle>
                  <dialogSize>440,400</dialogSize>
                  </root>
            </object>
         </componentClass>
      </object>
   </extension>

This works normally in the HTML Client, but not in the Flex Client where HTML portlets are not supported (we cannot have multiple iFrames in a scrollable summary page).

In the Flex Client the default behavior is to display a link that pops up a modal dialog with the desired content. The dialog's title and size are configured in the extension declaration in plugin.xml. The link reads Click to see [dialog title], as shown in the picture below. Please note that the modal dialog can be moved around but not resized.

Sample running in Flex client 6.0

Workaround to display a portlet in both Flex and HTML Client

See the sample vsphere-wssdk-html for an example of plug-in with two implementations of the same portlet. The main drawback of this workaround is that it forces you to keep a "hybrid" plugin, but since portlets are small UI components the added code should remain simple.

Custom object views

View extensions for custom objects are supported the same way as with vSphere objects. See the chassisA-html and chassisB-html samples.

We don't recommend to use summarySectionViews extensions in a custom object Summary view. Instead you should define one summaryViews extension and manage the summary content as you see fit.

   <!-- Chassis summary view -->
   <extension id="com.vmware.samples.chassisb.SummaryView">
      <extendedPoint>com.vmware.samples.chassisb.summaryViews</extendedPoint>
      <object>
         <name>(not used)</name>
         <componentClass className="com.vmware.vsphere.client.htmlbridge.HtmlView">
            <object>
               <root>
                  <url>/vsphere-client/chassisb/resources/chassis-summary.html</url>
               </root>
            </object>
         </componentClass>
      </object>
   </extension>

The example below uses JQuery UI's accordion widget which is well suited for a view with separate summary data boxes. See the chassisA-html or chassisB-html samples.

Data Access

If you are familiar with the Flex API the standard way to get data in a Flex view is through DAM (Data Access Manager) and its various DataRequest events. In an HTML view script you are free to call the server the way you want! The HTML Client SDK shows you the Ajax and Spring MVC patterns to follow in order to make your design and your code simple. A DataAccessController source code is provided as well to be used as is or to customize further.

Some key points:

Setting up a REST api for object properties

The SDK samples use the following pattern:

  1. The javascript code makes an Ajax get request with this URL: /[plugin-context-path]/rest/data/properties/[objectId]?properties=[properties-list]
    where [objectId] is the current context object id passed as parameter to the HTML view, and [properties-list] a comma-separated list of properties to be retrieved for that object.
  2. The UI plugin's web.xml is configured to map /rest/* requests to springServlet under its context path:
       <servlet-mapping>
          <servlet-name>springServlet</servlet-name>
          <url-pattern>/rest/*</url-pattern>
       </servlet-mapping>
    
  3. The UI plugin's bundle-context defines a dataAccessController bean, available on the plugin service side:
       <bean name="dataAccessController" class="com.vmware.samples.mvc.DataAccessController" />
    
  4. The plugin's java service bundle contain the DataAccessController class, with its @RequestMapping set to /data. It has a generic getProperties() method mapped to /properties/{objectId} which will handle the Ajax request above.
    /**
     * A controller to serve HTTP JSON GET requests to the endpoint "/data".
     */
    @Controller
    @RequestMapping(value = "/data", method = RequestMethod.GET)
    public class DataAccessController {
    ...
       @RequestMapping(value = "/properties/{objectId}")
       @ResponseBody
       public Map<String, Object> getProperties(
             @PathVariable("objectId") String objectId,
             @RequestParam(value = "properties", required = true) String properties) throws Exception {
    
  5. getProperties() uses a QueryUtil class to transform the list of properties into the proper Data Service query and make the calls. Results are returned to the browser as JSON data.
          Object ref = _objectReferenceService.getReference(objectId);
          String[] props = properties.split(",");
          PropertyValue[] pvs = QueryUtil.getProperties(_dataService, ref, props);
          Map<String, Object> propsMap = new HashMap<String, Object>();
          propsMap.put(OBJECT_ID, objectId);
          for (PropertyValue pv : pvs) {
             propsMap.put(pv.propertyName, pv.value);
          }
          return propsMap;
    
  6. When the Ajax call returns the javascript code can display the data as needed.

So DataAccessController and QueryUtil are very generic and can be copied as is in your plugin service bundle. They can also be shared between different UI plugins, as long as those plugins reference the correct bean in bundle-context.xml.

List extensibility

List extensibility is achieved the same way as with the standard SDK, i.e. without using any Flex components! The extension point com.vmware.ui.lists.ColumnSetContainer was deprecated in version 5.5, you can define your column extension completely in plugin.xml. Here is an extract of the chassis-app sample code:

   <!-- Define the chassis list columns -->
   <extension id="com.vmware.samples.chassisa.list.sampleColumns">
      <extendedPoint>com.vmware.samples.chassisa.list.columns</extendedPoint>
      <object>
         <items>
            <!-- Chassis name column -->
            <com.vmware.ui.lists.ColumnContainer>
               <uid>com.vmware.samples.chassisa.column.name</uid>
               <dataInfo>
                  <com.vmware.ui.lists.ColumnDataSourceInfo>
                     <headerText>#{name}</headerText>
                     <!-- Object property whose text value will be displayed (array of 1 element) -->
                     <requestedProperties>
                        <String>name</String>
                     </requestedProperties>
                     <sortProperty>name</sortProperty>
                     <exportProperty>name</exportProperty>
                  </com.vmware.ui.lists.ColumnDataSourceInfo>
               </dataInfo>
            </com.vmware.ui.lists.ColumnContainer>
            etc...

The requestedProperties values are fetched directly by the platform, i.e. no UI code is involved. You just need to implement a DataProviderAdapter or PropertyProviderAdapter on the java side to return the data.

3. Actions and Modal Dialogs

Action extensions are implemented using the standard extension point vise.actions.sets. Two types of actions are supported: UI actions and headless actions. Modal dialogs can also be opened directly from an html view for various purposes.

UI actions

UI actions always open a modal dialog box, for instance a confirmation popup before deleting an object or a wizard to perform some task. It is up to this UI code to call the server and perform the corresponding headless action or to use other service calls.

Actions are defined in plugin.xml using the ActionSpec delegate class com.vmware.vsphere.client.htmlbridge.HtmlActionDelegate. Here is how the chassis sample does it:

  <extension id="com.vmware.samples.chassisa.listActionSet">
      <extendedPoint>vise.actions.sets</extendedPoint>
      <object>
         <actions>
            <com.vmware.actionsfw.ActionSpec>
               <uid>com.vmware.samples.chassisa.createChassis</uid>
               <label>#{chassis.createAction}</label>
               <icon>#{addChassis}</icon>
               <delegate>
                  <className>com.vmware.vsphere.client.htmlbridge.HtmlActionDelegate</className>
                  <object><root>
                     <actionUrl>/vsphere-client/chassis/resources/editChassisAction.html</actionUrl>
                     <dialogTitle>#{chassis.createAction}</dialogTitle>
                     <dialogSize>500,400</dialogSize>
                     <dialogIcon>#{addChassis}</dialogIcon>
               </root></object>
               </delegate>
               <privateAction>true</privateAction>
            </com.vmware.actionsfw.ActionSpec>
         </actions>
      </object>
    </extension>

The implementation works as follows:

  1. The ActionSpec delegate field uses the class com.vmware.vsphere.client.htmlbridge.HtmlActionDelegate.
  2. actionUrl value points to the HTML content to be displayed. It is either a relative path starting with the plugin context (when the HTML content is part of your plugin), or an absolute https URL for external content.
    The actionUrl relative path must end with .html in order for the http session to be authenticated.
  3. The dialogTitle, dialogSize and dialogIcon properties apply to the dialog box: dialogTitle can be localized, dialogSize is the width and height in pixels, dialogIcon is an optional icon resource.
    dialogTitle must be present otherwise the action is treated as headless!
  4. When the action is invoked the platform opens a modal Flex dialog displaying the HTML content. The following parameters are added to the actionUrl:
  5. The dialog box can be closed either with a click in top right X button or by calling WEB_PLATFORM.closeDialog().

You should call WEB_PLATFORM.closeDialog in the code handlers for the dialog's Submit and Cancel buttons. If you don't implement that logic the only way to close the dialog is through the top right close button. Note however that your dialog code won't receive any Close event and it won't be possible to prevent its immediate dismissal.

Within the dialog script you can make REST calls to get or post data, for instance using the data access api to get object properties: /[plugin-context-path]/rest/data/properties/[objectId]?properties=[properties-list].

You can also invoke headless actions by using WEB_PLATFORM.callActionsController(url, jsonData), where url has the form /[plugin-context-path]/rest/actions.html?actionUid=[actionUid] (i.e. similar to the actionUrl used in plugin.xml for headless actions, but with the actionId argument spelled out).

Headless actions

These actions are intended to call the server directly and return some data, without any initial user interaction. Those use cases are rarer but still possible.

For instance here is how a delete chassis action can be declared in plugin.xml if you don't want any user confirmation prior to invoking the action:

   <extension id="com.vmware.samples.chassisa.actionSet">
      <extendedPoint>vise.actions.sets</extendedPoint>
      <object>
         <actions>
            <com.vmware.actionsfw.ActionSpec>
               <uid>com.vmware.samples.chassisa.deleteChassis</uid>
               <label>#{chassis.deleteAction}</label>
               <icon>#{deleteChassis}</icon>
               <delegate>
                  <className>com.vmware.vsphere.client.htmlbridge.HtmlActionDelegate</className>
                  <object><root>
                     <actionUrl>/vsphere-client/chassis/rest/actions</actionUrl>
                  </root></object>
               </delegate>
            </com.vmware.actionsfw.ActionSpec>
      ...

The implementation works as follows:

  1. The ActionSpec delegate field must use the class com.vmware.vsphere.client.htmlbridge.HtmlActionDelegate.
  2. The actionUrl property starts with the bundle context path or can be an absolute https URL.
    (When using an absolute URL you will also need to set up a cross-domain policy file on the target server).
  3. The platform makes a POST request to the server using the actionUrl and the following parameters:
  4. On a java side an ActionsController class handles the /actions.html URL mapping . The recommended pattern is to use the controller class as a simple dispatcher and call service methods based on the action uids. See how the chassis-h5service sample uses the same chassisService that comes with the Flex version:
       @RequestMapping(method = RequestMethod.POST)
       @ResponseBody
       public Map invoke(
                @RequestParam(value = "actionUid", required = true) String actionUid,
                @RequestParam(value = "targets", required = false) String targets,
                @RequestParam(value = "json", required = false) String json)
                throws Exception {
          ...
    
          ActionResult actionResult = new ActionResult(actionUid, RESOURCE_BUNDLE);
    
          if (actionUid.equals("com.vmware.samples.chassisa.editChassis")) {
             boolean result = _chassisService.updateChassis(objectRef, chassisInfo);
             actionResult.setObjectChangedResult(result, "editAction.notFound");
    
          } else if (actionUid.equals("com.vmware.samples.chassisa.deleteChassis")) {
             boolean result = _chassisService.deleteChassis(objectRef);
             actionResult.setObjectDeletedResult(result, "deleteAction.notFound");
    
          } else if (actionUid.equals("com.vmware.samples.chassisa.createChassis")) {
             Object result = _chassisService.createChassis(chassisInfo);
             if (result != null) {
                actionResult.setObjectAddedResult((URI)result, "samples:Chassis", null);
             } else {
                // Case where the name is already taken
                String[] params = new String[] { chassisInfo.name };
                actionResult.setErrorMessage("createAction.nameTaken", params);
             }
    
          } else {
             String warning = "Action not implemented yet! "+ actionUid;
             _logger.warn(warning);
             actionResult.setErrorLocalizedMessage(warning);
          }
          return actionResult.getJsonMap();
       }
    
  5. The invoke method must return a map containing a "result" key with the actual result value which will be converted into a JSON object. A utility class ActionResult is provided to define additional result parameters:

Modal Dialogs

UI actions described above display one type of modal dialog in response to a menu click or toolbar button. You can implement other types of pop-ups specific to your application within an object view or global view but they are constrained to the view boundaries and are only modal within that view. The API openModalDialog() added in version 6.0 allows to open a modal dialog box at the application level: it is similar to a UI action dialog except that it is called directly from an HTML view.

Here is a code snippet from the sample chassisA-html, file chassis-summary.js:

   // Example of a modal dialog opened outside the HTML view, not constrained in size,
   // for instance a wizard for which you need the rest of the application to be blocked.
   $("#editChassis").click(function() {
      // The objectId parameter is required only because editChassisDialog.js uses
      // WEB_PLATFORM.callActionsController to perform an action on that object.
      var url = "/chassisa/resources/editChassisDialog.html";
      WEB_PLATFORM.openModalDialog("Edit Chassis", url, 500, 300, objectId);
   });

In that sample we re-use the same kind of dialog as the editChassis action extension. Modal dialogs can interact with the back-end with any type of service calls. It is also possible to call back the parent view through javascript functions (see the callback example in chassis-summary.js).

openModalDialog calls cannot be nested, i.e. you must not call openModalDialog() again from the view displayed by openModalDialog (use a regular Javascript popup instead). Also, in the Flex Client, since the dialog is modal at the application level the view's content disappears temporarily until the dialog is dismissed.

If you prefer not to use openModalDialog() but use a regular Javascript popup inside the view, be aware of the following issue: the popup is only modal within the view. A user can click elsewhere in the application without the view being notified, and if the user returns to that view you are responsible to restoring the same state.

4. Global Views

Global views are views that are not attached to a specific object context, for instance the main view of an application or an admin view for editing some global configuration.

A global view is created with the extension vise.global.views. The componentClass is the same com.vmware.vsphere.client.htmlbridge.HtmlView as with object views. The url points either to a local HTML document (recommended) or uses an absolute https URL and the same restrictions applies.

   <!-- Global app main view -->
   <extension id="com.vmware.samples.h5.globalview.mainView">
      <extendedPoint>vise.global.views</extendedPoint>
      <object>
         <name>#{app.name}</name>
         <componentClass className="com.vmware.vsphere.client.htmlbridge.HtmlView">
            <object>
               <root>
                  <url>/vsphere-client/globalview/resources/mainView.html</url>
               </root>
            </object>
         </componentClass>
      </object>
   </extension>

Since there is no context object the global view document is opened with a request containing only the locale parameter.

vCenter selector

When a plugin view needs to display data for a particular vCenter a common UI is to use a vCenter Selector at the top, i.e. a drop-down listing all connected vCenters connected and allowing the user to switch quickly between them.

The HtmlView includes this selector when the flag <showVCenterSelector> is set to true:

   <extension id="com.vmware.samples.htmltest.vcSelectorView">
      <extendedPoint>vise.global.views</extendedPoint>
      <object>
         <name>#{app.name}</name>
         <componentClass className="com.vmware.vsphere.client.htmlbridge.HtmlView">
            <object>
               <root>
                  <url>/vsphere-client/htmltest/resources/vcSelectorTest.html</url>
                  <showVCenterSelector>true</showVCenterSelector>
               </root>
            </object>
         </componentClass>
      </object>
   </extension>

In that case the document request contains the following parameters, which can in turn be passed to a Java service in order to access vCenter data through the vSphere Web Service SDK.

If the view does not use an absolute https URL the sessionId and the serviceUrl are not part of the document request but can be obtained from the user session. In the test view below the parameters are simply printed out in response to a vCenter selection.

5. Other APIs

Navigation request

To navigate to another view you can use WEB_PLATFORM's sendNavigationRequest method, which is similar to the NavigationRequest API in Flex:

      // Navigate to a global view using its extensionId
      WEB_PLATFORM.sendNavigationRequest("com.vmware.samples.htmltest.settingView");

      // Navigate to an object view, so the objectId is required.
      WEB_PLATFORM.sendNavigationRequest("com.vmware.samples.chassisManageView1", objectId);

User session

WEB_PLATFORM.getUserSession() returns an object with the current user session information in case you need to use it on the UI side. The userSession object contains the following properties that are equivalent to the Java API: username, locale, serversInfo. serversInfo holds the list of vCenter servers connected to the Web Client. You can use it for building your own vCenter selector UI in place of the built-in selector described above.

Please note that samlTokenXml is not included in the javascript userSession object. You must use the Java API in order to access the SSO user credentials via the samlTokenXml property. See the vSphere Single Sign-On documentation.

Client type and client version

getClientType() and getClientVersion() were introduced in 6.5 and are backward compatible with 6.0 (see the implementation in web-platform.js).

Global refresh

A click on the top bar's Refresh button triggers a DataRefreshInvocationEvent in the Flex app. Active HTML views can be notified of that event by calling WEB_PLATFORM.setGlobalRefreshHandler with the document's local refresh function, like this:

   $(document).ready(function() {

      function refreshData() {
        // load the page data
        ...
      }

      // Set refreshData to be called when the user hits the top bar's Refresh button.
      WEB_PLATFORM.setGlobalRefreshHandler(refreshData);
   }

6. Internationalization

Internationalization is usually done on the client side since there is where most of the text and images being displayed originate from. But there are cases where a plugin needs to return text from the server side, see the "Server side resources" section further below.

For globalization purpose your plugin should support the same locales as the Flex and HTML Clients: en_US, de_DE, fr_FR, ja_JP, ko_KR, zh_CN and zh_TW. It is not recommended to support languages that are not in this list, because users would be confused to see your plugin views in one language while the rest of the UI stays in english.

Client side resources

String and image resources are defined in one or more .properties files included with the plugin's .war bundle. The defaultBundle attribute at the top of plugin.xml references the main properties file and is added automatically by the Plugin generation script.

Resources for HTML plugins are also compiled as Flex bundles by the build script to keep plugins compatible with the Flex client which expects the .swf format. If you don't compile resources as .swf files and try to run your plugin in the Flex client you will get an Unable to load resource module error.

Resources are referenced differently in plugin.xml and in Javascript or HTML files. The user browser's locale is what determines which language to load among the supported languages.

Resources in plugin.xml

The plugin.xml file contains many labels, view names, action names, etc. They should all be localized properly and not hard-coded in a particular language. Use the syntax #{RESOURCE_KEY} when the string or icon resource is defined in the defaultBundle. Otherwise use the syntax #{BUNDLE_NAME:RESOURCE_KEY}.

Note that the current locale is defined implicitly in plugin.xml.

Here is an excerpt of chassisA's plugin.xml, with defaultBundle set to "com_vmware_samples_chassisa" because the properties file is locale/en_US/com_vmware_samples_chassisa.properties.

It is important to use fully qualified bundle names in order to avoid clashes with other plugins!

<plugin id="com.vmware.samples.chassisa"
   defaultBundle="com_vmware_samples_chassisa">

   <resources>
      <resource locale="{locale}">
         <module uri="locales/chassisa-{locale}.swf"/>
      </resource>
   </resources>
  ...
   <templateInstance id="com.vmware.samples.lists.allChassis">
      <templateId>vsphere.core.inventorylist.objectCollectionTemplate</templateId>
      <variable name="namespace" value="com.vmware.samples.chassisa_collection"/>
      <variable name="title" value="#{chassisLabel}"/>
      <variable name="icon" value="#{chassis}"/>
  ...

The english version for ''chassisLabel'' and the ''chassis'' icon are defined in locale/en_US/com_vmware_samples_chassisa.properties like this:

# ------- String properties --------

chassisLabel = ChassisA
summary.title = Chassis main info
...

# ------------- Images -------------

chassis = Embed("../../assets/images/chassis.png")
localizedImage.url = assets/images/localizedImage-en_US.png
...

Resources in Javascript and HTML views

In case of HTML views or Javascript code the correct string resources must be injected at runtime. You can use the ns.getString() API defined in web-platform.js, where "ns" is the plugin's namespace shortcut. That API refers to the defaultBundle for convenience, here is how it is defined in chassisA's web-platform.js:

   var ns = com_vmware_samples_chassisa;
   ns.getString = getString;

   // Get a string from the resource bundle defined in plugin.xml
   function getString(key, params) {
      return WEB_PLATFORM.getString("com_vmware_samples_chassisa", key, params);
   }

And here is how the chassisA sample uses JQuery.text() to set the h3 title at runtime:

   // insert the localized value of "summary.title" in the #title node
   $("#title").text(ns.getString("summary.title"));

   <body>
      <!-- Static HTML text is replaced at runtime by localized text -->
      <h3 id="title">Chassis main info</h3>
      ...

There are other ways to load the proper string resources depending on the Javascript framework you are using. Refer to your framework's documentation.

Server side resources

There may be cases where your plugin is returning text from the server side, i.e. the java layer. For instance object properties that must be displayed in "readable form", or error messages coming from your back-end server. The plugin code is responsible for getting the current user's locale and returning the translated text for that locale.

For errors your back-end server may already provide localized messages. In other cases you can rely on the standard Java localization process, adding .properties files to your .jar file and loading the proper text based on the locale id.

Here is the UserSession API to access the current locale id:

   // see the vsphere-wssdk-service sample for injecting  _userSessionService in your class
   UserSession userSession = _userSessionService.getUserSession();
   String locale = userSession.locale;
   ...

Another option is to treat the server-side text as resource keys and defer the localization to the client side. For instance if you want to display the VM power-state the vSphere API only gives you enums ("poweredOff", "poweredOn", "suspended"). Instead of translating those on the Java side use the enum values as resource keys on the client side and keep everything in the same .properties file as the rest of your UI strings.

7. API Summary

This section reviews the new APIs and outlines which parts of the standard SDK are still relevant.

plugin.xml metadata

All SDK extension points are supported (except vise.dispose.namespace.inclusions which only applies to Flex UIs).

Otherwise everything else still applies to plugin.xml: localization with a resource bundle, extension filtering, etc.

HtmlView

This is the Flex object to use in plugin.xml for all HTML view extensions. At minimum it requires a url parameter pointing to the relative path of the view document as shown below.

   <extension id="com.vmware.samples.vspherewssdk.vm.monitor">
      <extendedPoint>vsphere.core.vm.monitorViews</extendedPoint>
      <object>
         <name>#{monitorTab.label}</name>
         <componentClass className="com.vmware.vsphere.client.htmlbridge.HtmlView">
            <object>
               <root>
                  <url>/vsphere-client/vspherewssdk/resources/vm-monitor.html</url>
               </root>
            </object>
         </componentClass>
      </object>
   </extension>

Properties supported by HtmlView:

HtmlActionDelegate

This is the Flex object to use in plugin.xml for action extensions. An action can either open a modal dialog box (UI action) or call the server directly (headless action). Here is an example of a UI action:

  <extension id="com.vmware.samples.chassisa.listActionSet">
      <extendedPoint>vise.actions.sets</extendedPoint>
      <object>
         <actions>
            <com.vmware.actionsfw.ActionSpec>
               <uid>com.vmware.samples.chassisa.createChassis</uid>
               <label>#{chassis.createAction}</label>
               <icon>#{addChassis}</icon>
               <delegate>
                  <className>com.vmware.vsphere.client.htmlbridge.HtmlActionDelegate</className>
                  <object><root>
                     <actionUrl>/vsphere-client/chassis/resources/editChassisAction.html</actionUrl>
                     <dialogTitle>#{chassis.createAction}</dialogTitle>
                     <dialogSize>500,400</dialogSize>
                     <dialogIcon>#{addChassis}</dialogIcon>
                  </root></object>
               </delegate>
               <privateAction>true</privateAction>
            </com.vmware.actionsfw.ActionSpec>
         </actions>
      </object>
    </extension>

Properties supported by HtmlActionDelegate:

Javascript APIs

Most of them are accessible through the WEB_PLATFORM variable which encapsulates the Web Platform services and is defined in web-platform.js.

WEB_PLATFORM APIsArgumentsDescription
callActionsController(url, jsonData) url: like /vsphere-client/chassis/rest/actions.html?actionUid=...
jsonData: the optional data used by the action.
Used to invoke a headless action from within a UI action dialog.
The target objectId(s) will be added automatically to the url.
closeDialog() Allows to close a dialog that was opened through a UI action, for instance after submitting a form.
getActionUid() Get the id of the action invoked to open a UI dialog or wizard. This id can be used in the url argument of callActionsController().
getActionTargets() Get the comma-separated list of object ids selected for an action, or null for a global action. This is valid only within a UI action dialog.
getClientType() Returns "flex" when the plugin runs in the Flex Client and "html" when it runs in the HTML Client.
getClientVersion() Returns a version in format "6.0", "6.5.0", "6.5.1" etc.
getObjectId() Get the context object id within an object view or a modal dialog. This object id can be used in turn to retrieve object data from the server.
getString(bundleName, key, params) bundleName: plugin resource bundle.
url: string resource key.
params: optional array of values to replace placeholders {0}, {1}, ... in the string.
Retrieve the localized value of a key defined in the plugin resource bundle.
See also getString(key, param) defined in your plugin namespace variable.
getVcSelectorInfo() Returns the info provided by the vCenter Selector added in the HtmlView. Properties are: serviceGuid, sessionId, and serviceUrl.
getUserSession() Returns the current user session info. Properties are: userName, locale and serversInfo.
Note that usually it is better to access UserSession with the Java UserSessionService.
setGlobalRefreshHandler(callback) callback: JS function name Use with the view's refreshData function, it will be called when the user clicks on top Refresh button.
openModalDialog(title, url, width, height,
objectId)
title: the dialog title.
url: url to the html content.
width, height: the width and height in pixels
objectId: (optional) id of the target object
Since 6.0. Open a modal dialog from within an html view, for instance a wizard which needs to block the rest of the UI and not be limited to the view. Because the dialog is modal at the application level the view's content disappears temporarily until the dialog is dismissed.
sendModelChangeEvent(objectId, opType) objectId: the object id if the view is an object view.
opType: "add", "change", "delete" or "relationshipChange"
Raise an event to update the object model after something changed. Note that this can be done directly through a headless action, see the chassis-app sample.
sendNavigationRequest(targetViewId, objectId) targetViewId: the view extension id,
objectId: the object id if the view is an object view.
Open a global view or object view. Similar to the Flex NavigationRequest API.
setDialogSize(width,height) width, height: the width and height in pixels. Since 6.0. Change the dialog size at runtime (i.e. overrides the dialogSize value defined in plugin.xml)
Not supported in HTML Client SDK 6.5
setDialogTitle(title) title: the dialog title. Since 6.0. Change the dialog title at runtime (i.e. overrides the dialogTitle value defined in plugin.xml)

 

A few APIs are "utilities" defined in the plugin namespace variable created in web-platform.js.

plugin namespace APIsArgumentsDescription
buildDataUrl(objectId, propList) objectId: the objectId passed as parameter to the view
propList: an array of property names to be retrieved.
Creates the REST url to use to retrieve a set of object properties through the DataAccessController.
Use WEB_PLATFORM.getObjectId() to get the objectId.
getString(key, params) url: string resource key.
params: optional array of values to replace placeholders {0}, {1}, ... in the string.
Retrieve the localized value of a key defined in the plugin resource bundle.

 

Java services and controllers

There is no prescription for the type of Java controllers required to support REST calls. The DataAccessController and ActionsController classes used in the samples are a recommended pattern. The Spring Web MVC integration is provided for both UI and Java bundles.

Keeping Java Services and Data Adapters separate from the controllers is also a recommended design. The service and data level integration should remain independent of the UI technology. The SDK Java APIs are the same for both type of client plugin.

Mapping of Flex APIs

If you are familiar with the Flex APIs here is a list showing their equivalent APIs in the HTML/Javascript world.

See also: Getting Started with HTML Client SDK - Eclipse Setup - Extensions Points - FAQ - Java API - Tutorial