In the application presentation logic, the UI navigation is the first thing that influences the user experience (UX). The APS package of an application completely defines the application UI navigation and all components involved in UI. This document explains general principles. There are more documents providing navigation specifics in CP and UX1.
On the server-side of the user panels, there is a navigation engine that identifies the plugged application UI navigation trees and helps the user panel render the combined navigation panel for a user.
In this document:
APS allows creating custom UI in the form of a navigation tree that can plug into various types of user panels. Every user panel has its own placeholders (sockets) to which a custom UI navigation tree can plug.
The following examples illustrate the navigation trees plugged into various user panels.
PCP:
CCP:
UX1:
In an APS package, the custom UI is presented by the following parts:
Metadata in the APP-META.xml
file must define one or more navigation trees plugged into
specified placeholders of various control panels.
Every navigation tree can contain one or more navigation items, which together make up the skeleton of the navigation tree.
Every navigation item may contain one or more view components. Each view is mapped to a screen in a CP. A view meta declaration can declare some navigation controls, for example, the Cancel, Previous, and Next navigation buttons.
A view source is a combined HTML and JavaScript code. The user panel places a view inside an HTML IFrame. In UX1, the single-page technology places all views of an application into a single IFrame.
In the IFrame, a view provides all necessary widgets to a user for managing application resources. Among them, there are internal controls processed by the application code and the navigation controls. By default, the latter are processed by the user panel. However, an application view usually has event handlers that redefine the behaviour of the navigation controls.
A navigation tree “tells” a user panel where a certain view must be embedded. A custom UI can be represented by a navigation tree similar to this diagram.
It contains a number of navigation items: Servers, Network, IP Addresses, and Firewall. The diagram also specifies views in some navigation items and the source files implementing the views. For example, the Servers item contains several views:
The direct view “servers” displays a list of servers and provides the toolbar widgets implementing
the New, Start, Stop, and Delete buttons. The servers.html
or servers.js
file implements the view.
Since the creation of a VPS, started from the “servers” view, may require more than one step,
a separate view is declared for each step, for example, the “server-new-1” view is used in the first step.
A source code, for example, server-new-1.html
or server-new-1.js
file implements the view.
The same way, the other items and views are implemented.
The APP-META.xml
file declares all needed navigation trees along with their internal static structure.
The view HTML or JS files and all auxiliary files are shipped inside the ui/
folder of the APS package
as presented in the following example.
Key notes:
In CP, every view source is an HTML file.
In UX1, every view source is a JavaScript file, and there is one HTML file, bootstrapApp.html
,
for the whole application UI in that user panel.
In UX1, along with regular views, you can also use view-plugins to embed them into other applications.
Static navigation is declarative, as the navigation tree structure, in this case, is declared in metadata of the APS package.
A navigation tree contains various types of UI navigation elements:
The <navigation> element is the navigation root in the tree. Different types of user panel can visualize this element differently. For example, in CP, when using an APS predefined placeholder, it is represented by a top level tab.
An <item> element creates a tree branch bound to the <navigation> element or to another upper item, its parent. The <navigation> element and several <item> elements create the tree skeleton. There are no loops in the tree. In a CP, an item is visualized as a tab if it has a view as a direct child under it.
Note
If an item does not have a direct child view, it is not visualized in the navigation tree. If there are child items (with their own child views) under this item, they will move up and replace the parent item in the navigation tree.
A <view> element corresponds to a screen in a user panel. It is bound to an upper <item> or to another upper <view>. The one that is bound to an <item> will be activated when the item tab is selected in a user panel.
Warning
To avoid ambiguity in the static navigation:
Do not create more than one direct child view under an item.
Do not plug a view to more than one item, no matter those items are in the same or in different navigation trees.
Note
A view can have child views that will be available only via Dynamic Navigation. As always, the children will inherit the variables declared by their parent.
A <group> element is similar to an <item>. The only difference is that it is not visualized and for this reason it must not have a direct child view. You can use it to group some items in order to assign the same variable for them or plug them all to a certain placeholder.
UX1 contains more navigation elements.
A UI navigation element can contain the following internal elements and attributes:
The <navigation>, <item>, and <group> elements can declare a <placeholder> element to reserve a place where other navigation elements of the same or other applications can plug. It must have a unique ID in the form of URI. To plug a navigation element to another element, the <plugs-to> element of the former must refer to the <placeholder> ID of the latter.
Note
In CP, one application is able to plug its navigation part to a navigation tree of another application only if resources of both applications are in the same subscription.
Warning
To avoid ambiguity, the placeholder IDs must be unique in scope of the integrated applications.
A <var> element inside a navigation element declares a variable that represents a singleton resource or a collection of resources that the navigation engine must get from the APS controller and then pass to the view it is going to activate. A navigation element may have several variables declared. A variable can be optional or required. In the latter case, if the variable cannot be resolved neither the navigation element nor its child elements are rendered on the screen.
Note
A variable declared in a navigation element is available for all its child elements throughout the hierarchy. This peculiarity in many cases substantially affects the overall UI performance and must be carefully considered during design as discussed later in the Performance section.
A view declaration often contains declaration of navigation controls, such as <Cancel> and <Submit>. The control panel will automatically render them when activating the view and then processes the clicks on the buttons by its internal handlers. However, the view can contain its own event handlers for the navigation buttons to redefine their default behavior.
An optional visible
attribute of a UI navigation element defines explicitly if the element must be visible on
the screen. Typically, the attribute refers to a logical operation that analyses the declared navigation variables
and returns true
or false
.
Any navigation element must have at least one parent because it is always declared inside a navigation tree. Additionally, you can add a parent using the <plugs-to> element. Since the parents can have their own parents, the element can get many ancestors in different branches leading to different navigation roots.
There is an exception for the navigation root, that is the <navigation> element. You define its parent only by means of the <plugs-to> element. Sometimes, a <navigation> element does not have a parent at all and it is used to collect other elements that are plugged into various parents using the <plugs-to> element.
In the following diagram, the “srvA” view is declared in the “misc” tree and it plugs into the “services” view of another tree.
Parent-child relationship defines the most important navigation processes, which are Element Visibility and Resolution of Variables.
Note
From the visibility and inheritance perspective, only one of the upstream ancestor branches is active for a navigation element. General rules that define the active upstream branch are:
An ancestor branch an element is bound by <plugs-to> takes precedence over the initial static branch where the element is declared. In the above diagram, the “svrA” view gets access to its own variable “resourceA” and to all variables inherited from its ancestors in the “ccp” tree, but it does not have access to the “resourceMisc” variable declared in the “misc” tree.
To avoid ambiguity, it is not recommended to place an element under more than one ancestor branch. The element will inherit only the first of the ancestor branches where it is bound by <plugs-to>.
Note
In other words, to ensure an element is in a single ancestor branch, it must be either, in one tree plugged into a placeholder or it plugs into a <placeholder> using only one <plugs-to>.
In a user panel, the passed navigation path up to the current navigation element is the navigation context that is presented
as the context
array in navigation requests and responses.
Dynamic navigation is imperative, as it implies navigation from a view to another view by calling a navigation method and specifying the destination view ID. It is possible to use two different types of dynamic navigation.
Single page navigation is available only in the UX1 mode. In this case, only one IFrame is used for all views. Every view is a JavaScript module based on the APS JS SDK and loaded in the IFrame statically or by using the aps.apsc.gotoView() method.
Navigation within multiple pages is based on using a separate HTML file for each view. When navigating to
a view by calling the aps.apsc.gotoView()
method, another IFrame is loaded with the required view.
Every time a user opens the control panel or navigates from one view to another, the browser sends a request to the navigation engine to get the data necessary to render the navigation panel on the left-hand side and the target view on the right-hand. The developer tools of a browser help you identify a request as in the following example.
Suppose a customer admin clicks on the item presenting the application services that is the application navigation root:
This causes a REST request similar to the following (some data is intentionally missed out for brevity):
POST /aps/2/navigation?bw_id=c3027.....
"placeholder": "http://www.parallels.com/ccp/2"
"itemId": "http://aps-standard.org/samples/suwizard1p#ccp"
"context": []
"parameters": {}
"locale": "en_US"
The request payload shown above contains the following:
placeholder
informs the navigation engine about the placeholder ID where the target element is plugged. This is
the UX1 itself.
itemId
is the navigation element ID, the navigation root ID of the application in the example. The ID
consists of the APS application ID and the navigation ID as declared in the navigation tree metadata.
context
identifies the current source navigation element from which the request is sent. In the example, the
request was sent from the top level, so the context is empty.
parameters
is a named list of parameters passed to the target from the source.
locale
specifies the current user locale chosen when the user logged in to user panel.
The response will look similar to this:
{
"context":[ ],
"items":[
{ /* Home dashboard */ },
{ /* User management */ },
{
"children":[
{
"children":[
{
"id":"http://aps-standard.org/samples/suwizard1p#servers",
"src":"/aps/2/packages/8018b116-...-1604b56e2c6b/ui/servers.js",
"label":"Servers",
"vars":{/* ... */},
"maximized":"false",
"type":"view",
"package":{/* ... */}
}
],
"id":"http://aps-standard.org/samples/suwizard1p#servers",
"label":"Servers",
"type":"item",
"package":{/* ... */}
},
{
"id":"http://aps-standard.org/samples/suwizard1p#vps-creation-wizard",
"label":"VPS Creation Wizard",
"type":"item",
"package":{/* ... */}
}
],
"id":"http://aps-standard.org/samples/suwizard1p#ccp",
"label":"VPS Management",
"type":"navigation",
"package":{/* ... */}
},
{ /* One more custom application */ },
{ /* Domain management */ },
{ /* Account management */ }
]
}
The received message contains the full path to the target view as well as the navigation roots of the other system built-in and custom applications. The “suwizard1p” application is presented by the navigation root labeled “VPS Management” that contains two items, labeled “VPS Creation Wizard” and “Servers”. The latter contains the target view labeled “Servers”. This illustrates a search for the nearest visible view for the case when the navigation root of an application is selected by a user.
The <navigation>, <item>, and <view> elements are visualized in a user panel. In the normal case, when all considered elements are under the same navigation root, the following conditions prevent the visualization of an element:
At least one of the navigation variables required in this element explicitly or inherited from its ancestors is not resolved as explained in Resolution of Variables.
The visible
attribute of the element is resolved to false
.
The element is an <item> that does not have the direct view or the direct view cannot be visible due to one of the above conditions.
If an element is not visible due to an unresolved variable or the visible
attribute, none of its child
elements in the downstream branches will be visible.
In its response to the navigation request, the navigation engine returns only visible elements necessary to render the left-hand side navigation panel and the target view in the main window.
Note
When a target element is a <navigation> or an <item>, the navigation engine tries to find a proper view in the downstream branches to render in the main window. The first priority is the direct view. Then goes the first visible view the engine finds out starting from the first downstream branch.
If the user panel navigates a user to a view that cannot be visualized due to one of the above-mentioned reasons, in return to the respective navigation request the user panel receives the “404 Not Found” response.
Every time the navigation engine receives a Navigation Request, it resolves the variables needed for the target view in one of the applications and for computation of the visibility of the navigation roots in the other applications. This includes the following steps:
From the target element up, identify all ancestors in the upstream branch up to the navigation root.
Going backward from the root down to the target element, try to resolve all declared variables.
If at least one of the required variables is not resolved, the navigation process stops and an error 404 message is returned.
If the computed visible
property of any ancestor is false
, the navigation process stops and an error 404
message is returned.
If the target element is a <navigation> or an <element>, find the view to render in the main window. This process also includes resolving of the variables declared in the downstream path to the view.
To define the visibility of an application navigation root in UX1, the navigation engine for the sake of performance resolves only those variables that influence the visibility of that navigation root:
The variables declared as required directly inside the <navigation> element.
The variables used in the visible
attribute directly inside the <navigation> element
and downstream to the view that must be rendered in the main window.
If a required variable is not used in the visible
attribute, the navigation engine only defines if the
mapped resource or resource collection is not empty by adding the limit(0:0)
filter to its request to the APS
controller.
Warning
From the last visibility concept, it follows that the application top most navigation element
can be visible in the UX1 menu although none of its child elements is visible. This leads
to the 404 error message when a user clicks on that navigation element in the menu. In the following example,
if the mbox
variable (required by default) is not resolved, the top most navigation item will show up in the panel,
but when a user clicks on it the panel will return the 404 error message.
<navigation id="mycp-ux1" label="MailService">
<plugs-to id="http://www.aps-standard.org/ui/user/2"/>;
<view id="MBoxConfig" label="MailBox Configuration" src="ui/ccp2/mycp/mycp.js">
<var name="mbox" type-id="http://isv1.aps-demo.org/samples/mail/mbox/1.0"/>;
</view>
</navigation>
To avoid the last effect, ensure the application top most navigation element is not visible in a case when none of its child elements is visible. For example, the above meta declaration can be updated as follows:
<navigation id="mycp-ux1" label="MailService">
<var name="mbox" type-id="http://isv1.aps-demo.org/samples/mail/mbox/1.0"/>;
<plugs-to id="http://www.aps-standard.org/ui/user/2"/>;
<view id="MBoxConfig" label="MailBox Configuration" src="ui/ccp2/mycp/mycp.js">
</view>
</navigation>
In this case, if the mbox
variable is not resolved, the navigation item will not be visible in the user panel menu bar.
From the performance perspective, the declaration of navigation variables to represent some resources is the preferable way as compared with getting the resources directly from a view. While the latter affects the view performance when a user works in the view, the former affects the performance of the navigation within the user panel. Even though the APS team does its best to improve the navigation algorithm, there are still chances to radically decrease the performance of an application UI and even of the whole user panel by an improper declaration of navigation variables.
The above-mentioned navigation algorithm implies the following rules to help you keep the UI performance at an acceptable level:
Declare variables directly for the view that must process the resources presented by those variables.
The visibility of an element, especially of a <navigation>, is determined faster by declaration of a required
variable than by the visible
attribute, since
the former needs to only define the existence of the mapped resource while the latter requires the analysis of one or
more properties of one or more variables.
The heavy-loaded views requiring navigation variables mapped to huge number of resources must be placed on the lowest level of the navigation tree.
If you have to place a heavy-loaded view on the same level with a light-weight view, the latter must be in the upper position inside the meta declaration.
In a user panel, a custom UI view loaded into an HTML IFrame receives a special object called view context (do not confuse with the navigation context) whose structure looks as in the following example.
aps.context =
{
view: //the current view
{
id: "viewId"
},
vars: {domains: {}, users: {}, /* ... */}, //an object containing resources representations
// declared by variables, for example, domains and users defined
//in APP-META, in the navigation section
varsMeta: {domains: {range: "items */0"}, users: {range: "items 0-2/123"}, /* ... */ },
params: {}, //an object containing the custom view parameters that have been passed
// to the view using the aps.apsc.gotoView method
locale: "en_US", // Locale id in the language_TERRITORY form
/* ... Other data */
}
The objects defined in the view context are available for the JavaScript code in the view file. For example, the varName1 variable is available as aps.context.vars.varName1.
Note
In CP, the aps.context
object is available in a view code if the aps.ready! object from APS JS
SDK is required in the file.
In UX1, the aps.context
object is available in a view code starting with phase onContext
.
In UX1, the aps.context
object has the varsMeta
object containing a short presentation of every
navigation variable available for the current view. A variable presentation contains the range
property as in the
following example:
The range
value is the same as the value of the APS-Skip-Content-Range header.
It allows determining the currently available range (two first numbers) and the total number of resources (last number)
stored in the APS database.
The above code snippet uses the last position in the range
to get the number of resources in the database.
A system control panel embeds the application navigation tree and renders the selected view using several steps outlined below for support teams:
Get the request parameters:
View ID
Resource ID
View parameters
Subscription ID
Send a POST request to /aps/2/navigation
in the APS controller with request parameters.
In the response, get the JSON representation of the navigation tree including the required view - menu items, view ID, and representations of resources declared by variables in the navigation section.
Render item elements as menu elements. For example, in CP, a navigation item is presented by a tab.
Initialize the aps.context
JS object with the navigation variables, locale, and other.
This object will be passed to the target IFrame and parsed by the aps/ready!
plugin.
Render the selected view element as an IFrame using the aps.hub.createManagedHub
method:
aps.hub.createManagedHub(
"https://<APSC_endpoint>/aps/2/packages/<package id>/ui/myView.html",
mashupAreaDiv
);
In the following screenshot, the servers.html
file and the required by it the displayError.js
file
are included into the IFrame.