Shell-App

Scope of functions of the shell app

The shell app constitutes an external framework for displaying, navigating and interacting with multiple resources. With the shell app, you can align the operating concepts of the various resources.

Technical background

The shell app provides access to the d.api interface. Apps can use this API for navigation within the shell app, for instance.

Using the API functions

Below you can learn about the different options for using the shell app interfaces to meet your needs.

Displaying a resource in the shell app

To display a resource in the shell app, you must embed the d.api in the head element of the resource using a script tag. When the resource is loaded, a check is performed during the initialization of the d.api to determine whether a shell app has already been loaded around the resource.

If no shell app is found, the d.api attempts to call the shell app under https://_SYSTEMBASE_URI_/shell and passes on the relative path of your resource as a fragment. After that, the shell app is loaded and then loads the resource originally called as the first resource using the URI fragment. The resource is reloaded, which also initializes the d.api again.

Since a shell app is or was already loaded, this shell app is referenced in the d.api and the function calls are passed on to the d.api.

The d.api is delivered via a CDN (content delivery network). You can embed a specific version as follows:

<html>
  <head>
    ...
    <script src="https://cdn.service.d-velop.cloud/dapi/v2.13.1/dapi.js"></script>
  </head>
  <body></body>
</html>

You can embed the latest version as follows:

<html>
  <head>
    ...
    <script src="https://cdn.service.d-velop.cloud/dapi/latest/dapi.js"></script>
  </head>
  <body></body>
</html>

info: For a specific version, a cache header of one year is used because these files no longer change.

For the latest version, the cache header is set to 30 seconds so that changes are transferred after 30 seconds at the latest.

Changing the title of a resource

You can use the d.api function dapi.publishTitle(title: string) to change the title of a resource at any time.

dapi.publishTitle("new resource title");

info: If an empty string is transferred (for example, if a value to be displayed is not entered), then is displayed in the old shell app instead of the empty string.

Changing the URI of a resource in the browser history

You can use the d.api function dapi.publishLocation(uri: string) to change the URI of your resource in the browser history.

dapi.publishLocation("new resource uri");

info: The URI must be absolute.

Displaying a global dialog

You can use the function dapi.openDialog(uri: string, height: number, width: number, absoluteValues?: boolean) to display a global dialog. A global dialog is displayed throughout the entire shell app. For this purpose, you must specify a URI for the dialog contents as well as the height and width of the dialog. If you transfer the value true for the optional parameter absoluteValue, the values for the height and width are interpreted as absolute values (that is, they are translated directly into pixels).

dapi.openDialog("/app/dialogresource.html", 30, 40);

This call generates a dialog with a height of 30 percent and a width of 40 percent.

dapi.openDialog("/app/dialogresource.html", 300, 400, true);

This call generates a dialog with a height of 300 pixels and a width of 400 pixels.

warning: You should use global dialogs only if the contents of the dialog relates to the entire shell app and its content For contents that relate to the resources, you should use dialogs within the resources. This ensures that users know the resource to which the dialog belongs.

info: The values transferred for the height and width are translated into the appropriate CSS classes while generating the dialog. As a result, the actual values used in the browser may differ from the values transferred in the function. If the value true is entered for absoluteValue, the values are used directly as pixel values.

To close the dialog, users can simply click the backdrop next to the dialog (the area in the background).

To close the dialog after the user has performed an action, your app must call dapi.closeDialog(). If this function is called without displaying a dialog, nothing happens.

Starting a download from a resource

The d.api function dapi.download( href: string ) is used to start a download in a hidden iFrame.

info: The download can only be triggered by main or secondary resources. It does not matter whether the main resource is in full-screen mode.

Determining whether a resource is currently the main resource

You can use the d.api function dapi.isMainResource() to check whether your resource is currently the main resource. The function returns a boolean value.

You can trigger the navigation to a different resource using a simple anchor tag that adds the value dapi_navigate in the target property.

<a href="/path/to/new/resource" target="dapi_navigate">other resource</a>

You can also use the function for anchor tags containing child items.

<a href="/path/to/new/resource" target="dapi_navigate">
  <div>
    <p>Click me!</p>
  </div>
</a>

Of course, you can also use the function dapi.navigate(uri: string) to trigger a navigation from JavaScript.

dapi.navigate("/path/to/resource");

warning: You cannot navigate directly within the sidebar using the dapi. To navigate out of the sidebar, use the mechanism for exchanging data between the sidebar and the displayed resource.

Closing a resource

You can use the d.api function dapi.closeResourceIfMainResource() to close a resource via API.

dapi.closeResourceIfMainResource();

The function is executed only if the calling resource is displayed as the main resource. The property dapi.isMainResource() therefore has to return the value true. Otherwise, nothing happens.

Preventing a resource from being closed

You can use the d.api function dapi.setClosable(false) to define whether a resource can be easily exited. You can register a callback that is called someone wants to close the resource (for instance, in a confirmation dialog). In this confirmation dialog, for instance, users can then confirm that they really want to leave the resource.

dapi.setClosable(false);
dapi.setInterruptNavigationCallback((invokeFinish) => {});

If you want to enable them to leave after confirming the dialog, for instance, your app must call the functions dapi.setClosable( true ); and invokeFinish() in succession. The shell app transfers this function as a parameter automatically. The shell app then tries to close the main resource again. However, the callback can also just be a simple output stating that the resource cannot be exited.

info: Please use setClosable() or setInterruptNavigationCallback() (the function setInterruptCloseCallback() is not supported for this application scenario).

Displaying a resource in full-screen mode

You can use the d.api function dapi.activateSingleResourceMode() to display a resource in full-screen mode.

dapi.activateSingleResourceMode();

warning: To switch back to your original set layout, always use this function with the dapi.deactivateSingleResourceMode() function.

Exiting full-screen mode

You can use the function dapi.deactivateSingleResourceMode() to exit full-screen mode and switch back to your specific layout.

dapi.deactivateSingleResourceMode();

warning: Use this function only if you switched to full-screen mode beforehand using the function dapi.activateSingleResourceMode(), to ensure that the specific layout that you set is retained.

Receiving a notification when your resource is visible

You can use the d.api function dapi.setResourceVisibilityChangedCallback(callback: (isResourceVisible: boolean) => void) to transfer a function that is called whenever the visibility of the resource changes to the shell app. This allows you, for instance, to trace whether the resource is displayed or hidden.

dapi.setResourceVisibilityChangedCallback((isResourceVisible) => {
  if (isResourceVisible) {
    console.log(`the resource is visible`);
  } else {
    console.log(`the resource is invisible`);
  }
});

During the call, a parameter is passed to the callback as a boolean value. The parameter contains the value true if the resource is visible and false if it is not displayed. For example, you can use this call to suspend the automatic update of the resource while the resource is hidden.

Useful information to know about context actions

The shell app includes the concept of context actions. These are actions that can all be used in relation to the displayed resource. There are navigating and non-navigating actions. A navigating action, for instance, is the switch from a document to its parent dossier.

There are also non-navigating context actions. These context actions trigger actions within the displayed resource that are applied only to the resource itself. For example, marking a task as read is a non-navigating action.

Deploying context actions

You can register context actions in the header of the resource using the link tag.

<html>
  <head>
    ...
    <link
      rel="ctxaction"
      type="text/html"
      href="/Shell-App/docs/details.html"
      title="context action name"
      data-icon="/relative/path/to/ctxAct.png"
    />
  </head>
  <body></body>
</html>

You can also register context actions from JavaScript. For this registration, use the d.api function dapi.setContextActions(contextActions: IContextAction[]). You can pass an array of context actions that are then displayed for users to this function. A context action can have the following attributes:

interface IContextAction {
    /**
     * URI of navigation target
     */
    href?: string;

    /**
     * Function to execute if contextAction is invoked.
     * @returns {}
     */
    callback?: () => void;

    /**
     * Name of the function to execute if contextAction is invoked.
     * NOTE: A function with the corresponding callbackName must be registered first and currently only bridges can register such functions. It is not possible for regular resources.
     */
    callbackName? : string;

    /**
     * Arguments for the callback invocation.
     */
    callbackArgs?: any[];

    /**
     * Title of ContextAction
     */
    title: string;

    /**
     * URI of icon
     */
    icon: string;

    /**
     * Open the reference target of the context action in the outer supply.
     */
    openInOuterSupply?: boolean;
}
dapi.setContextActions([
  {
    type: "text/html",
    href: "relative/path/to/resource[.html]",
    title: "ContextActionName",
    icon: "relative/path/to/ContextActionIcon.png",
  },
]);

You can also use JavaScript to register non-navigating context actions.

dapi.setContextActions([
  {
    title: "Say hello",
    icon: "relative/path/to/ContextActionIcon.png",
    callback: (args) => {
      alert("Hello " + args[0] + "!");
    },
    callbackArgs: ["you"],
  },
]);

If you click the Say hello context action, a notification dialog with the message "Hello you!" is displayed.

Info: The attribute callbackArgs is optional and can also include the value undefined.


Hiding context actions

To hide context actions, call the function dapi.setContextActions() with an empty array.

dapi.setContextActions([]);

Using d.api events

Notifying a resource about changes

When the status of a resource has changed or a resource has been deleted, other resources containing content with a dependence on this resource must be informed. This helps to ensure that these other resources can update themselves, for example.

You can use the function dapi.dispatchResourceEvent(event: IResourceEvent) to dispatch a corresponding event. Other resources can then respond to it if they have registered with these events. For this purpose, you must create a ResourceEvent event. The event expects the event type (changed or deleted) and the URI of the resource as parameters. The event is then sent to the registered event listener with the dispatchResourceEvent function. The URI of the resource is required to ensure that registered event listeners can decide whether the event is relevant or can be ignored.

Responding to changes in other resources

You can respond to changes in other resources by using the function dapi.addResourceEventListener(eventType: string, callback: (event: IResourceEvent) => void) to register with ResourceEvent events. The event type has the following attributes:

interface IResourceEvent {
    /**
     * Name of the event.
     * - changed : the resource has been changed
     * - deleted : the resource has been deleted
     */
    name: string;

    /**
     * uri of affected resource.
     */
    uri: string;
}

When a relevant event is dispatched, the callback is called. In it, you can then respond to the changes to the resource or the deletion.

As an example, let’s assume we have a resource /app/todos.html in which tasks are listed. Clicking this task opens a d.api navigation to the relevant data resource.

The resource /app/todos.html has registered the following event listeners for resources:

dapi.addResourceEventListener("deleted", function (resourceEvent) {
  var $todos = dux.query("tr");

  $todos.each(function (idx, todo) {
    var link = dux.query(todo).data("href");

    if (link === resourceEvent.uri) {
      dux.query(todo).remove();
      dapi.navigate("dapi_blank");
    }
  });
});

dapi.addResourceEventListener("changed", function (resourceEvent) {
  var $todos = dux.query("tr");

  $todos.each(function (idx, todo) {
    var link = dux.query(todo).data("href");

    if (link === resourceEvent.uri) {
      var $task = dux.query(todo).children("td").eq(1);

      if (!dux.query(todo).hasClass("text-muted")) {
        dux.query(todo).addClass("text-muted");
        $task.html('<p class="small">Done</p>' + $task.text());
      }
    }
  });
});

The detail resource /app/task1.html contains a Delete context action that triggers a deleted event. This event calls the following function:

dapi.dispatchResourceEvent(
  new dapi.ResourceEvent("deleted", window.location.pathname)
);

There is also a Task completed button that calls the following function when clicked:

dapi.dispatchResourceEvent(
  new dapi.ResourceEvent("changed", window.location.pathname)
);

When you click Task completed, the item in the list that references/app/task1.html is marked as complete in the /app/todos.html resource.

When you click the Delete context action, the list item is removed and you are brought to an empty resource.

info: The implementation is based on the implementation of event processing in the browser (see Creating and triggering events).

Using the new operating concept

Displaying a resource in the new operating concept

The new shell app now has a more modern new operating concept. This concept is based on Google’s Material Design, so we recommend using Material Design for the resources as well.

A resource is displayed in the new operating concept if the attribute window.nextGenerationApp contains the value true in the header.

<script>
  window.nextGenerationApp = true;
</script>

You must add this attribute before embedding the d.api.

Using the sidebar

The sidebar is the area displayed at the edge of the shell app.

Displaying custom content in the sidebar

Each resource can define a page as content for the sidebar. You can use the d.api function dapi.setSidebarContent(uri: href) to display content in the sidebar.

dapi.setSidebarContent("/app/Sidebar.html");

You cannot navigate within the sidebar using the dapi. To trigger navigation from the sidebar, use the mechanism for exchanging data between the sidebar and the displayed resource.

Clearing the sidebar

You can clear the sidebar to reset it to its state before the content was added. To clear the sidebar, use the function dapi.setSidebarContent(uri: href) with the URI about:blank:

dapi.setSidebarContent("about:blank");

Opening the sidebar

You can use the d.api function dapi.openSidebar() to open the sidebar. This function expands the sidebar.

Minimizing the sidebar

You can use the d.api function dapi.closeSidebar() to minimize the sidebar. This function collapses the sidebar.

Responding to changes to the visibility of the sidebar

You can use the d.api function dapi.setSidebarVisualStateChangedCallback( callback: (visual: boolean) => void ) to respond to changes to the visibility. The callback is executed whenever the sidebar is opened or closed.

Exchanging data between the sidebar and the displayed resource

The d.api function dapi.transferDataObject( dataObj: object ) can be used to transfer any object from the sidebar to the main resource or from the main resource to the sidebar. You can use the d.api function dapi.addDataObjectListener( callback: (dataObj) => void ) to respond to this transfer.

Using the inner supply (display area)

The inner supply is displayed next to the main resource. It may be an open task, for example. The inner supply is located below the top app bar in the shell and contains an app bar that is automatically named with the title of the selected resource (e.g. the title of a task). You can use the inner supply for content that relates directly to the main resource (such as a detail view, for example). The content should only be for display purposes wherever possible, and should not contain major actions. If you require such actions or direct further navigation, use the outer supply.

info: It is not possible to navigate from the inner supply. The view serves only to display additional information and is itself not a resource.

Opening the inner supply

You can open the inner supply using the d.api function dapi.openInnerSupply(uri: string).

dapi.openInnerSupply("/app/InnerSupply.html");

info: If the outer supply is still open, it is closed with this function.

Closing the inner supply

You can close the inner supply using the d.api function dapi.closeInnerSupply().

Using the outer supply (display area)

The outer supply is also located next to the main resource; in contrast to the inner supply, however, the relevant content is displayed beyond the app bar It serves as a kind of minor resource. The content must not reference the main resource directly. It is possible to navigate from the outer supply.

Opening the outer supply

You can open the outer supply using the d.api function dapi.openOuterSupply(uri: string).

dapi.openOuterSupply("/app/OuterSupply.html");

info: If the inner supply is still open, it is closed with this function.

Closing the outer supply

You can close the outer supply using the d.api function dapi.closeOuterSupply().