Dynamicaly load routes

I am starting to build a project and I am at stage of selecting right framework for that. Aurelia looks good and simple. I like it. And I have questions.

I’ am writing sort of CMS like Joomla but with this SPA technologies and built-in advanced CCK. The likeness of Joomla in regard of extensions. I want 3d party developers to be able to extend functionality of my application very easily. The end users install those extensions through sort of extensions market. It simply copy client and server side to user application instance. One extension is one folder.

Now about routing. I need sort of dynamic routs loader. I’ll have only one URL pattern for my application. /[ext_name]/* . The first segment will be always a name of an extension. Even home page will be an extension /home . So the / url will be always redirected to /home . I want to be able to parse URL, get first segment and load all routes of that extensions.

Lets say there is URL /page/25 so I know this is page extension and it has path /ext/page/index.js that contain all routes and loads all required components of that extension.

I want that developers define routes with config.map the Aurelia way and not invent my own convention for that.

When Aurelia was released first I did a test and that was successfull. I tried to this recently without any lack. I believe it is because Webpack. I sould use any loading system without webpack. Although it is strange and unlogical. Why cannot create a bundle of only first files needed and then load the rest dynamicaly. I am not familiar with webpack but I thought that is what it is doing but it looka like it is alternative loading technology, and it does not support dynamic loads.

Would it be possible to achieve? In documentation I see that I can navigationStrategy or Unknown routes that have potential in my opinion but I am not sure.

Would you say if what I want will be possible and if yeas what are keywords? Where to look for the answer?

As you can imagine, there are a lot of tools in Aurelia to approach this problem, and it’s hard for me to recommend the best one without being much more familiar with your application. I’ve written an article that enumerates all the approaches to dynamic routing, here: http://davismj.me/blog/dynamic-routing/. That should give you some ideas of where to start.

From what you’re describing, I’m wondering if page might be best created as a plugin. Another option to explore.

If all your widgets/ pages are reachable / accessible at build time, then webpack can handle the job for you, with ease:

import { PLATFORM } from 'aurelia-framework';

// the following enable code splitting for page/25 widget
// which mean even you build them all,
// none will be in the main bundle, thus avoid slowing down startup
PLATFORM.moduleName('page/25', 'page_25_bundle');

export class App {
  configureRouter(config) {
      { route: 'abcd', moduleId: 'page/25' }

Combine this with solutions described in @davismj 's answer , or mapUnknownRoutes, you will achieve what you want.

If plagin may have their own routes why not.

I looked through this and I am not sure this gives me any idea.

I need to navigate to the page that was not there when application was built. All I know that URLs are build acording to convention I declare. In URL I can get any information I want.

If you don’t know what file will be used as your view model, then you cannot use webpack. It is a bundler, not a loader. I’d suggest using requirejs, which is supported by Aurelia CLI

I had problems with dynamic routes that were Webpack related. Hopefully there will be a Webpack-less version of Aurelia for Net Core. For the interim, the following may be of interest (Storing/Retrieving Navigation Menus/Route in Database)

Can you direct me to an example?

I thought one can use webpack with requirejs. Webpack bundle all initial files and from that point all new models are loaded with requirejs or commonjs or systemjs.

Suppose you have following structure

├ 📂components
│  ├ foo.js
│  └ foo.html
├ 📂routes
│  ├ home.js
│  └ home.html
├ app.js
├ app.html
├ main.js
├ ...

and all initial files mean

  • src/main.js
  • src/app.js
  • src/app.html
  • src/components/foo.js
  • src/components/foo.html
  • src/routes/home.js
  • src/routes/home.html
    What do you mean by new models?
├ 📂extensions
│ ├ 📂users
│ │ ├ 📂components
│ │ │ ├ UserCard.js
│ │ │ ├ UserCard.html
│ │ │ ├ UsersList.js
│ │ │ └ UsersList.html
│ │ ├ index.js
│ │ └ routes.js
│ ├ 📂pages
│ │ ├ 📂components
│ │ │ ├ PageIntro.js
│ │ │ ├ PageIntro.html
│ │ │ ├ PagesList.js
│ │ │ └ PagesList.html
│ │ ├ index.js
│ │ └ routes.js
│ └ 📂home
│   ├ 📂components
│   │ ├ HomePage.js
│   │ └ HomePage.html
│   ├ index.js
│   └ routes.js
├ app.js
├ app.html
├ main.js
├ ...

I antisipate the structure about like this. I am not sure but that what I want. Every folder inside extensions folder is an extension. It has it’s own set of routes. Important that all files of extension is in one folder and not that there is routes folder there there is a file for routes for all extensions.

In any way, extension may be added to application running instance later, abter application is built. When I add extension, I add to DB, list of possible menu items with routes to that extension, so when app is launched it request API and get menu structure. So in menu there might be new items of just installed extension.

My initial idea is to make a convention that all URLs of the extension have to strart or have first segment the name of the extension like /home/* or /page/* and using this first segment I can load required extension and let router to continue.

Somethign like beforeRoute I get first segment and import router.js of extension in the first segment. This router.js contain router map. So then when router continue it already know what module (by module mean in router you use terminologi moduleID:… ) to load.

I want 3d party developers to be able to extend functionality of my application very easily.

Now about routing. I need sort of dynamic routs loader. I’ll have only one URL pattern for my application. /[ext_name]/*

From what you described, I think it is doable with requirejs. It would require the root routing table in your app.js to be separately generated though, I guess. So every time after you add an extension, you can regenerate the routing-table.json. About the routing, it can be very dynamic with requirejs/amd, but it depends on what you want, because you could trigger a full application rebuilt, if you want to use webpack. I think there are a lot of folks who have done this, which is probably one of the reasons they went with Aurelia.

If you are still uncertain, I put together a demo to demonstrate dynamic routing capability aurelia-dynamic-cms https://codesandbox.io/s/ywyj60p9qj?module=src/app.js

1 Like

Your example is perfect for what I need. Thank you.

So will it work with webpack or what system?

So every time after you add an extension, you can regenerate the routing-table.json .

I am on the stage of architecturing and pre-planning. I do not know how exactly I will solve the task so I investigate and I can adopt. For example I can make and actualy plan a convention for every extension developer create map of routes in JSON format. I also will have API endpoint where I merge all those data and create map of all possible routes. This will be used by user to create navigation structure.

So, yes on the moment of app loading to brouser I may get from JSON API the list of menu structure with all routed with all links to component per each route etc…

1 Like

If you missed my question in long answer, the example you show, will work with webpack or what loading system?

It depends on what you do when a new extension is added for a used. Do you rebuild for that user, or you want to let the loader figure it out at runtime? If you rebuild everything on every extension change, then webpack can handle the job, otherwise, I think you will need to employ something else.

I di dnot plan to rebuild. The app I develop wil be deployed without source already built with core extensions. Once new extension installed, it should work without a re-build.

Although, ofcource I can ship my application with source, install new extension into source folder and then build the app againg.

Although it soundds little complicated and also who knows how many and how heavy extensions might be installed. If extension is an URL it means auless user go there, there is no need to load it. Extensions might be pretty heavy. I would not like to preload all the app. It might grow very big, like hundreds of Mb of JS code.

So I am pritty sure I want to load extensions dynamicaly and not to prebuild app with extensions after install.

The code you gave me is working fine. What is it? What type of loader it uses?

It’s an EsmLoader, which means it use dynamic import API. Browser support table can be found here https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import (at the bottom)

What can I use to build it to increase browser support. As I understand I need dynamic loading and it is supported only by Chrome. Can I bould it with Babble to convert to ES5?

I think you can build to AMD and employ requirejs to enable it.