OOTHUB Blog Detail

  • Simplifying SmartUI Routers in OpenText Content Server_271.jpg

    Simplifying SmartUI Routers in OpenText Content Server

    OpenText Content Server's SmartUI framework allows developers to add custom URL routes, making pages linkable and easy to share. A typical custom route might appear as:

    https://myserver/otcs/cs.exe/app/my-custom-route/12345?arg1=abc&arg2=def

    To make such a route work, several pieces must come together: handling direct URL entry from bookmarks or external sources, navigating within the application through code, extracting URL parameters and query strings, and feeding that information to the view that renders the page. Tools like Vue Router handle this seamlessly, but SmartUI leaves you to build it all yourself. Every new route demands the same repetitive setup of module extensions, event binding, and manual URL assembly.

    A routing plugin removes this repetition by offering a straightforward configuration layer. You define a route in a few lines, navigate via a simple programming interface, and get URL parameters automatically delivered to your view.

    Adding a Route

    First, register the plugin with an extension orphan in the usual SmartUI fashion:

    {
      "csui/pages/start/perspective.routing": {
        "extensions": {
          "geniusui": ["geniusui/perspective.routers/my.perspective.router"]
        }
      }
    }

    The router inherits from rhcore/routing/rhcore.router.register, which expands upon the core csui/pages/start/perspective.router. Routes go inside the RHCoreRoutes array:

    define("geniusui/perspective.routers/my.perspective.router", [
      "rhcore/routing/rhcore.router.register",
      "geniusui/routes/my.route.view",
    ], function (RHCoreRouterRegister, MyGeniusView) {
      return RHCoreRouterRegister.extend({
        RHCoreRoutes: [
          { path: "my-custom-route", name: "myCustomRoute", view: MyGeniusView, meta: { somekey: "value1" } },
          { path: "my-custom-route/:id", name: "myCustomRouteId", view: MyGeniusView, meta: { somekey: "value2" } },
        ],
      });
    });

    Every route entry contains: path (the URL pattern after the base URL), name (a distinct identifier for code-based navigation), view (a Marionette view responsible for rendering), and meta (custom information sent to the view, handy when sharing a view across multiple routes).

    Consuming Route Information in Your View

    When the view is created, the router supplies contextual data through options.rhcore:

    define("geniusui/routes/my.route.view", ["csui/lib/marionette"], function (Marionette) {
      return Marionette.ItemView.extend({
        constructor: function (options) {
          Marionette.ItemView.prototype.constructor.apply(this, arguments);
          const { router, sessionData, route } = options.rhcore;
        },
      });
    });

    This object holds three items:

    • router — lets you move between routes programmatically. For instance, router.navigateTo({ name: "myCustomRoute", params: { id: "67890" }, query: { hello: 1 } }) sends you to /app/my-custom-route/67890?hello=1, and router.openDataId(12345) opens a specific Content Server node.
    • sessionData — includes the Content Server baseUrl, the support directory location, and an otcsticket for REST authentication (compatible with the @kweli/cs-rest package).
    • route — describes the matched route with id (the route name), params (URL segments like { "id": "12345" }), query (query parameters like { "arg1": "abc", "arg2": "def" }), and meta (the custom data from the route definition).

    Integrating Alternative Frameworks

    Marionette is not your only option for the view layer. Package your React, Vue, or Angular app as an ES6 module and load it on demand:

    Marionette.ItemView.extend({
      onRender: function ({ options }) {
        const { router, sessionData, route } = options.rhcore;
        import("./myApp.mjs").then(({ default: mount }) => {
          this.app = mount(this.el, { router, sessionData, route });
        });
      },
      onBeforeDestroy: function () { this.app?.unmount(); },
    });

    This keeps the SmartUI portion minimal while your main interface lives inside whichever framework you prefer. An added benefit is portability — the same app can function inside both SmartUI and the classic Content Server interface.