Second Stage of navigation?

I have cerate a routing in uibuilder like this:


// @ts-nocheck
/*globals UibRouter, uibuilder */

const routerConfig = {
    routeContainer: '#routecontainer',
    defaultRoute: 'lights',
    hide: true,
    routes: [
            id: 'lights', src: './routes/lights.html',  type: 'url',
            title: 'Lights'
        }, {
            id: 'power', src: './routes/power.html', type: 'url',
            title: 'Power'

const router = new UibRouter(routerConfig)


<nav id="nav-standard">
        <a href="#lights" onclick="router.doRoute(event)" id="b-one">Lights</a>
	<a href="#power" onclick="router.doRoute(event)" id="b-two">Power</a>

<div id="uibroutecontainer"><!-- router content will appear here --></div>

now i want a seconde stage of routing inside this pages, like node-red.local:1880/lights/livingroom and node-red.local:1880/lights/kitchen.

Can anyone tell me the best way to implement this or recommend a tutorial where it is explained?

The router capability of UIBUILDER is still fairly new and therefore services the most common use-cases, it wasn't designed (yet) as a fully comprehensive multi-layer router. I suspect that, for that, you might need a far bigger and more complex router solution?

At present, only a single router instance is allowed on a page as well since trying to allow for multiple routers was more complex than I had time for when I put together the router library.

So right now, I think you have a number of potential workarounds:

  • Flatten your routes by replicating, say, the outer HTML of the lights route across each room route html, then you only need 1 level of route.
  • Go a non-route method for the rooms - which is what you would have done without a client-side router solution. For example, using a tabbed style layout where you hide or display:none the rooms you don't want to see.
  • Create your own sub-router, dynamically loading and unloading the template like the router library does.
  • Rummage through the current router library and see if either:
    • You can work out how to allow multiple router instances - I'd be more than happy either for a PR or for some hints on what needs changing. Noting that I am currently working on UIBUILDER v7 which contains a few potentially breaking changes (for a few people) and so that is an ideal time to do an uplift on the router as well.
    • Or whether you can work out how to do mutli-level routes.

Thanks for the information @TotallyInformation !!!

For the moment i think i will use the non-route solution. I am not so firm in javasript that I could implement my own subrouter.

But I don't think multiple router instances are necessarily needed for nested routing. Nested routing can also be solved using an instance by specifying the sub-routes within the main routes.

Starting from such a configuration:

const routerConfig = {
    routeContainer: '#routecontainer',
    routes: [
            id: 'lights', src: './routes/lights.html',  type: 'url',
            title: 'Lights'
            subrouteContainer: '#lights-container' 
            subroutes: [
                    id: 'lights/kitchen',
                    src: './routes/lights-kitchen.html',
                    type: 'url',
                    title: 'Kitchen',
                { ... }

Of course, it is then necessary to adapt the router logic accordingly and possibly the handling of the navigation events, or am I seeing this wrong?

Hi David,

That was my other thought as well. I will certainly add it to the backlog since it would make the router more powerful and probably doesn't need that much extra logic.

At present though, I'm focused on trying to get UIBUILDER v7 ready. Going reasonably well but, as usual, while doing something yesterday I spotted a bit of a logic fail in the startup code for managing the front-end libraries. I believe I have a better solution so that's the next task.

Hmmm, so is that why UIB complains when I try to use a / in the root URL?

Ideally I like to use the first part of the url e.g. /fhir to be the router for all urls used in the "FHIR" application -- then serve all the UI pages under /fhir/ui/* and set up http in nodes at /fhir/api/* for the backend api processing (since in this case I'm not using your built-in api router).

In fact, I'd love to be able to use a mix of the backend api.js router and http in flow apis -- but I suppose at best that would cause unpredictable results with multiple routes using the same root, right?

No. But that has always been the case. What you are looking at there is something core to uibuilder itself rather than the front-end router library.

UIBUILDER has never allowed you to create a URL with a / as it would seriously mess up the ExpressJS server routing.

However, you can create FOLDERS in your front-end code folder and put more html files in there which gives a similar effect.

You can also set up uibuilder instance ExpressJS ROUTERS - these are specific to a uibuilder node and let you create dynamic routes at the server end rather than at the client end. You use the <instanceRoot>/routes folder for that. This feature was introduced in v6.5. Foolishly though, it looks like the only documentation for that is in the release note. You can also add instance-level ExpressJS middleware as well using the same feature. Another feature that allows is server-side html templating - using ExpressJS's templated view options.

A couple of ways to achieve that. Either use a reverse proxy (which should really always be your first port of call for doing complex URL, security, etc configurations). That will let you move whatever you like to wherever you like. If you need to separate all that out from Node-RED's native user URL's, you can always use a custom ExpressJS server as well - configure that in the uibuilder section of settings.js.

Or, if you don't mind Node-RED's core http-in nodes also getting the same URL prefix, simply change your httpNodeRoot in settings.js.

You can serve instance-level API's as well as server-side custom routes for your uibuilder node as mentioned above. API's have been available for a while and use the <uibInstanceRoot>/api/ folder.

Well, that's one of the main reasons UIBUILDER is fairly strict about URL naming. Because ExpressJS allows multiple routes to serve the same URL, yes it can become a nightmare to manage and debug.

However, you absolutely should be able to do what you want. For example, you could serve an API using http-in/-out nodes at /myurl/myapi and have a uibuilder node with a name of myurl, that absolutely should (probably!) work. :slight_smile:

Alternatively, you can have your myurl uibuilder node and use ~/.node-red/uibuilder/myurl/api/ and define your own ExpressJS API JavaScript file if you don't mind defining your own ExpressJS middleware. The required functions are native ExpressJS routes. Similarly, you can create generic server-side routing in ~/.node-red/uibuilder/myurl/routes/. The functions you create are actually the same type.

1 Like