Routing in a module [OLD BASE TEMPLATE]¶
Routing in an app that is embedded via module federation is a challenging task because, by design, a module is planned to function as a small sub-component and a whole application that is used to work separately from the main application.
To achieve a navigation system that works in harmony with the main application (HortiView), we need to control both routers:
- router of HortiView
- router of the module
Because, in most cases, routing in a single-page application is realized with a router solution like react-router, we have two routing solutions working simultaneously in the application in the following flow:
Example 1 | Open the module¶
- The user clicks on a link to a module.
- The HortiView-Router routes the user to the module's base path /farm/modules/[moduleid].
- The base template has an implementation/wrapper around the navigate function that always includes the base path to each route as a prefixed absolute path.
- The Module-Router checks its definition of routes and opens its root route /.
Example 2 | Navigate within the module¶
- The user clicks on a link in the module (that executes a routing/navigation function) /subpage.
- The navigate function executes the navigateFunc function, which represents the navigateTo function of the HortiView-Router.
- The HortiView-Router navigates to the new page /farm/modules/[moduleid]/subpage.
- The URL changes and the history is extended by the new route. Because HortiView doesn't have any route definition for this route except for the module's base path, it won't execute any routing logic and keeps the module loaded (due to an implementation of the module's base path with a wildcard).
- The change of the route/URL will change the Base-Property
currentNavigationPath
. - The property change will trigger the module's navigation again.
- The Module-Router calls its navigate function, and the router loads the implementation/component behind /subpage but with an implementation that doesn't change the browser's history.
Implementation Example¶
To implement a proper navigation, that works within your module and HortiView, we would highly recommend, to use the template solution, or atleast the modulebase.
The modulebase solves all navigation issues, by pre-implement and setup a react-router of the latest version.
ModuleBase to get react router¶
import { BaseProps, ModuleBase } from "@hortiview/modulebase";
import { ModulePadding } from "@hortiview/shared-components";
import env from "../../environments.json";
import { TranslationProvider } from "./providers/TranslationProvider";
import { ROUTES } from "./RouteConfig";
import { checkConfig } from "@hortiview/modulebase/dist/types/Environment";
const App = (customProps: Readonly<BaseProps>) => {
return (
<ModulePadding>
<TranslationProvider currentLanguage={customProps.currentLanguage}>
<ModuleBase props={customProps} routes={ROUTES} env={checkConfig(env)} />
</TranslationProvider>
</ModulePadding>
);
};
export default App;
Provide routes / RouteConfig¶
Every route can be configured in the most simple way:
export const ROUTES = [
{
path: "*",
element: <NotFound />,
},
{
path: "",
element: <Home />,
},
{
path: "testpage",
element: <Testpage />,
},
{
path: "example",
element: <Example />,
}
];
Its important, that the route-paths are relative (without slash in the beginning).
Navigate within the module¶
To navigate within a module, you can import the useNavigate
hook from the modulebase and use it exactly as React Router implements it:
Don't import from react-router
You can import all other React Router properties and methods that are typically provided by React Router.
It is important to import them from the modulebase and not directly from React Router.
import { Button } from "@element-public/react-button";
import { TypoDisplay } from "@element-public/react-components";
import { Group } from "@element-public/react-group";
import { useLocation, useNavigate } from "@hortiview/modulebase";
import { useTranslation } from "react-i18next";
export const Home = () => {
const navigate = useNavigate();
const { pathname } = useLocation();
const { t } = useTranslation();
return (
<div>
<Group>
<TypoDisplay>This is the pathname: {pathname}</TypoDisplay>
<Button onClick={() => navigate("/testpage")}>
{t("template.navigateToTestpage")}
</Button>
</Group>
</div>
);
};
Implementation [Old Template/Deprecated]¶
DEPRECATED
This part of the documentation is deprecated and is only valid for the old template. If you do not plan to use the base module/template, you can still gain insights into implementing proper navigation for your module.
The BaseTemplate contains 3 parts to make the navigation work correctly.
RouteConfig¶
Configuration of all possible routes, relative to the application.
RouteConfig.tsx | |
---|---|
Usage of RouteConfig¶
The configuration will be passed to the ModuleBase and used for the router implementation (of react-router-dom):
ModuleBase.tsx | |
---|---|
Creation of the Module-Router¶
The routing component has a pretty basic implementation of a router (the Module-Router):
Routing.tsx | |
---|---|
It uses the module's base path /farm/modules/[moduleid] to create a route that works in harmony with the HortiView-Router.
Combined Routing-Function¶
To finalize this setup, we configure a new routing function that implements the flow described in the two examples: