# Some background
The OpenText Content Server SmartUI makes it possible to extend the router with custom routes. It allows custom URLs, which are linkable, bookmarkable, and shareable.
Consider the following example of a custom route:
https://myserver/otcs/cs.exe/app/my-custom-route/1235?arg1=abc&arg2=def
In this example, we have 12345
as a URL parameter, and arg1=abc&arg2=def
as query parameters.
In order for a route to work, we need to be able to:
- navigate directly to the URL via a bookmark, page refresh, or link from an external location,
- programmatically navigate to the URL within SmartUI,
- parse the URL and query parameters, and
- provide the parsed data to the view for data loading and rendering.
Applications such as Vue Router (opens new window) and Nuxt (opens new window) handles most of this for you. You simply define the routes, its properties, and it works.
Adding a router in SmartUI requires you to write the management code yourself. It's a complex process of extending modules, interlinking them with listeners to react to change events, and writing code to generate the URL. It's complex and repetetive, which makes it highly error prone. Adding a new route requires a considerable amount of code.
To simplify this, I wrote a plugin, which abstracts the complex management parts into a simple interface. In particular::
- it defines a new route with a few lines of code,
- provides a simple API for programmatic navigation to the route, and
- automatically extracts the URL and query parameters to make the data available to the view.
Let me show you how it works.
# Simplifying the SmartUI router
As with any new route, you need to create a csui/pages/start/perspective.routing
extension (i.e., using an CSUI::CSUIExtension
orphan in your OSpace) to register the plugin with SmartUI. For this example, I named the extension geniusui/perspective.routers/my.perspective.router
.
{
"csui/pages/start/perspective.routing": {
"extensions": {
"geniusui": ["geniusui/perspective.routers/my.perspective.router"]
}
}
}
2
3
4
5
6
7
The router rules are extended from rhcore/routing/rhcore.router.register
. This is an extension of csui/pages/start/perspective.router
, which augments its functionality to handle the management of the route.
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" },
},
],
});
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
The routes are defined in the RHCoreRoutes
property, and for each route we have the following parameters:
path
- the url of the route, appended to the SmartUI base url (e.g.,cs.exe/app/my-custom-route
orcs.exe/app/my-custom-route/12345
),name
- a unique name for the route (useful for programmatic navigation),view
- a Marionette view (e.g., aMarionette.ItemView
instance), andmeta
- any extra data for the view (which is useful when using the same view in different contexts).
The router extension automatically passes additional metadata in the options
parameter of the view constructor, which is useful for rendering. For example:
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;
// ...
},
});
});
2
3
4
5
6
7
8
9
10
11
12
The options.rhcore
object contains three values:
router
- an object that can be used for programmatic navigation,sessionData
- session information, androute
- information about the current route.
For example, the router
object can be used for navigation:
// navigates to a named route, e.g., /app/my-custom-route/67890?hello=1
router.navigateTo({
name: "myCustomRoute",
params: { id: "67890" },
query: { hello: 1 },
});
// open a node, e.g., /app/nodes/12345
router.openDataId(12345);
2
3
4
5
6
7
8
9
The sessionData
object contains the Content Server baseUrl
, support directory path, and an otcsticket
. These can be used with @kweli/cs-rest (opens new window) to make REST requests to Content Server.
Finally, route
contains information about the route, which can be used to load data and render the view:
{
"id": "myCustomRouteId",
"params": { "id": "12345" },
"query": {
"arg1": "abc",
"arg2": "def"
},
"meta": {
"somekey": "value2"
}
}
2
3
4
5
6
7
8
9
10
11
# Usage with React or Vue
In a previous blog post, I discussed the possibility of using SmartUI with a modern framework such as React, Vue, or Angular. The idea is to compile the project to UMD for use in SmartUI. The approach is still valid, but the same can also be accomplished by compiling the project to an ES6 Javascript module, which can then be imported from SmartUI using an asynchronous import.
As before, the project should export a function to mount the application. The function can also accept context parameters, such as the sessionData
and route
discussed above. The router
object could also be passed in to allow SmartUI navigation from within the application.
For example, the view could look something like this:
Marionette.ItemView.extend({
onRender: function ({ options }) {
const { router, sessionData, route } = options.rhcore;
import("./myVueOrReactApp.mjs").then(({ default: mount }) => {
this.app = mount(this.el, { router, sessionData, route });
});
},
onBeforeDestroy: function () {
// cleanup
this.app?.unmount();
},
});
2
3
4
5
6
7
8
9
10
11
12
13
The protyping so far has shown this to work well. What's also great is that apps can be made universal, which allows them to work from either SmartUI or the classic UI. Perhaps I'll write a followup post about that sometime.