Router questions

I have some Aurelia 1 Web sites that I would like to start transitioning to Aurelia 2. Those sites use dotnet for the back end and Webpack for bundling. I decided to try using Vite for bundling this time because I have spent so much time dealing with Webpack issues during upgrades. So far I can get a shell application working with dotnet 9 and vite 6. It seems functional in a debug and a deployed state with a few minor rough edges.

The next step was to bring in more of the pieces used for style and navigation. The style part went ok, but navigation isn’t working that well. I don’t know if it was the correct decision, but I went with the router-lite. It seems to work as shown in the documentation to create a simple menu, but when I do things like add a routable link to a page, I am getting errors. I also ran into some limitations. The first limitation is that it won’t let me create a route that doesn’t have a component, which I used with Aurelia 1 to setup headings for drop-down menus.

So first question is can I configure a route similar to this from Aurelia 1?

{
                route: "config",
                name: "config",
                settings: {
                    dropdown: "config",
                    auth: ["Admin", "Setup Admin", "Back Office"],
                    icon: "fas fa-gears"
                },
                moduleId: "#",
                nav: true,
                title: "Configuration"
}, {
                auth: "true",
                moduleId: PLATFORM.moduleName("../material/MaterialList"),
                name: "materiallist",
                nav: true,
                route: "material-list",
                settings: {
                    dropdown: "config",
                    icon: "fas fa-coins",
                    auth: ["Admin", "Setup Admin", "Back Office"]
                },
                title: "material-list",
}

With Aurelia 1, I would use that to construct a drop down menu with a top-level of Configuration. With router-lite, it says a component is required.

The next question is with links embedded in a page. In the nav menu, links seem to work fine, but in a page they don’t seem to work. Am I doing something wrong here?

The navigation writes out this:

<li class="nav-item">
  <a href="register" class="active">
    <i class="fas fa-address-card">Register</i>
  </a>
</li>

In a page it writes out this:

<div class="mb-1">
    <a href="register">Register as a new user?</a>
</div>

Clicking on the nav link works fine with no error, but clicking on the link in a page throws an error unless I say open in a new tab, which also seems to work fine. It seems like it is looking for an “agent” and not finding it.

This is the error that is thrown:

route-context.ts:370 Uncaught (in promise) Error: AUR3174: Failed to resolve VR(viewport:'default',component:'not-found') at:
RC(path:'app',viewports:[VPA(state:currIsActive|nextIsScheduled,plan:'none',n:RN(ctx:'app/login',c:'Login',path:'login'),c:RN(ctx:'app/login',c:'Login',path:'login'),viewport:VP(ctx:'app',name:'default',default:'home',fallback:'not-found'))])
 RC(path:'app/login',viewports:[])
    at _RouteContext._resolveViewportAgent (route-context.ts:370:33)
    at route-tree.ts:542:27
    at onResolve (functions.ts:433:10)
    at route-tree.ts:538:14
    at onResolve (functions.ts:433:10)
    at createConfiguredNode (route-tree.ts:530:10)
    at createAndAppendNodes (route-tree.ts:458:88)
    at router.ts:792:46
    at Array.map (<anonymous>)
    at updateNode (router.ts:792:36)

From here:

public _resolveViewportAgent(req: ViewportRequest): ViewportAgent {
    if (__DEV__) trace(this._logger, Events.rcResolveVpa, req);

    const agent = this._childViewportAgents.find(x => { return x._handles(req); });

    if (agent === void 0) throw new Error(getMessage(Events.rcNoAvailableVpa, req, this._printTree()));

    return agent;
  }

Hi @elmt1!

Let us deal with first with the (seemingly easier) problem of links not working on a page, although it works from the navigation menu. I am assuming that these links are in one of the child pages from the root (the app). If this assumption holds, then you might need to use something like this:

<div class="mb-1">
    <a href="../register">Register as a new user?</a>
</div>

I am assuming here is a single level of routing hierarchy; that is, all routes are defined at the root. By default, the href (as well as the load custom attribute) assumes that the route is relative to the current component. As it does not find a register it errs. When using the load custom attribute, you can control the routing context, and the routes are treated relative to that.

Now, coming to the other problem, I am not really sure what was done in au1 with the route configuration without a module Id. I am unfortunately neither aware of such use-cases, nor I am sure if it is officially supported. It would be nice if you can point me to some documentation of this, so that I can understand it better. Having said that, if you need that just for the navigation dropdown, maybe use the navigation model directly instead of creating a dummy routing configuration.

Happy Au2 migration! :v:

Thank you for help. Changing the href=“…/register” fixed the first issue.

Regarding the second item, you are right, I may have just stumbled onto something that worked in Aurelia 1. I came up with the idea of adding dummy routes with no target so that they could be used as drop-down headings in a Bootstrap 5 menu, which is more constrained than the old Bootstrap 3 menu system. Aurelia 1 didn’t seem to care that the route was just a place-holder to group other routes.

Since I only ever needed 2 levels, it wasn’t too bad just making several passed through the routes using the built in settings data to build menus worked ok. Maybe I need to rethink that and add something that references the route but provides a hierarchical structure for building menus.

One more hopefully simple question. How do I create a route to an html only component? Something like an about page?

How do I create a route to an html only component? Something like an about page?

It seems that you are using conventions. In that case, you may try the following.

import { route } from '@aurelia/router-lite';
import * about from './about.html';

@route({
  routes: [
    {
      path: 'about',
      component: about,
    },
  ],
})
export class MyApp {}

The tooling around convention should convert the HTML to a custom element that can be registered.

However, I never tried to route to an HTML-only component. Thus, curious to know if it actually works for you. :slight_smile:

1 Like

Thank you for the suggestion. I couldn’t get that import to work. I tried to just inline the component and that didn’t work out well either. The closest I could come up with was something like this:

        {
            id: 'about',
            path: 'about',
            component: CustomElement.define({ name: 'about-page', template: '<template><h1>About Page</h1></template>' }),
            title: 'About',
            data: { icon: "fas fa-info-circle" }
        }

But that isn’t really practical. Creating a class for the page so that it can be a CustomElement is the only thing I found that works, but with Aurelia 1, I didn’t have to do that, I just had something like this.

            {
                moduleId: PLATFORM.moduleName("../home/About"),
                name: "about",
                nav: true,
                route: "about",
                settings: {
                    icon: "fas fa-circle-info"
                },
                title: "about",
            }

Maybe I just couldn’t get the syntax right, but creating a simple class to add the CustomElement isn’t difficult, it is just different that it was with Aurelia 1.

Hey @Sayan751. Tbh I am suffering from similar lack of control when it comes to the navigation context. However, documentation and customizability on @aurelia/router-lite seems to be superior to @aurelia/router, which is counter intuitive.

For example, using …/routeName does not seem to work. It always defaults to the root context, which leads me to having to define routes like /subRoute/routeName which causes the root viewport to re-render.

I guess more documentation or clarity is needed.

The HTML-only components can be used as routed components, it just needs a bit more work. Here is a working example.

You need to use register the components globally:

// main.ts
import Aurelia from 'aurelia';
import { RouterConfiguration } from '@aurelia/router-lite';
import { MyApp } from './my-app';
import * as home from './home.html';
import * as about from './about.html';

Aurelia.register(RouterConfiguration, home, about).app(MyApp).start();

And then just use the component names in the routing configuration:

// my-app.ts
import { route } from '@aurelia/router-lite';

@route({
  routes: [
    { path: '', redirectTo: 'home' },
    {
      path: 'home',
      component: 'home',
    },
    {
      path: 'about',
      component: 'about',
    },
  ],
})
export class MyApp {
  public message = 'Hello World!';
}

I agree that this is sort of departure from the common pattern of the examples in the docs. The example in my last post would have been more aligned. I will try in the coming days to make that example work.

1 Like

Do you mean that you have this problem with @aurelia/router? If yes, then can you consider switching to @aurelia/router-lite if the migration cost is not that high? In case, you are facing the issue with @aurelia/router-lite, can you please share a reproduction?

Well, just because something has -lite in the name, does not mean it should be taken litely :stuck_out_tongue: Bad puns apart, I would say that because the use-cases that @aurelia/router-litetargets to support is smaller than that of @aurelia/router, which contributes towards being documented thoroughly.

Feel free to contribute to the docs. As developers of the framework, it is not always possible for us to know how the other developers are using it. If you feel the docs are not sufficient, raise a bug or even better a PR. That’ll make us glad!

1 Like

Thank you for all the help so far. I have run into another routing question. I am trying to pass parameters to a router-lite route using a query string. The query parameters would be userId and code. I have tried various ways of defining the path and haven’t figured out how to define the parameters. I was able to get them to work as part of the path like confirm-email/:userId/:code, but I would prefer that they were query string parameters.

Searching randomly, I came across this pull request that implies I could define a queryParams array but I could find anything more complete. I also found an example that implied I could pull them from using the currentRoute, but the currentRoute path parameterInformation and query were all empty strings after attaching. Is there an example somewhere that shows how to pass and use query string parameters?

The path cannot be configured with query parameters are route parameters. Those can only be part of paths, as you have already noticed.

However, you can issue navigation instruction with query parameters. This is documented here (look for queryParams). Here is also a working example.