Developing Custom Modules#

This section explains how to develop custom modules for the BaSyx AAS Web UI.

Modules are the right extension mechanism when you want to implement larger, workflow-oriented features that go beyond a single Submodel or SubmodelElement visualization. Typical examples include import/export workflows, cross-submodel analytics, dashboards, or domain-specific tools.

What Is a Module?#

A module is a self-contained application feature that:

  • Has its own route under <web-ui-url>/modules/...

  • Appears as an entry in the Modules tab of the main menu

  • Runs inside the standard application shell (app bar, footer)

  • Has full control over its internal layout and navigation

Modules are typically used when:

  • Multiple submodels or submodel elements are involved

  • Business logic spans more than a single visualization panel

  • Dedicated workflows, forms, or dashboards are required

Where Modules Live#

All modules are located in:

src/pages/modules/

Simple modules#

A single Vue file placed directly in this directory automatically becomes a module:

src/pages/modules/MyModule.vue

This creates:

  • Route: /modules/mymodule

  • Menu entry: MyModule (or a custom title)

Larger modules#

For larger modules, use a dedicated directory with an index.vue entry file (recommended):

src/pages/modules/PcfProcess/
  ├── index.vue
  ├── routes.ts          ← optional nested route manifest
  ├── components/
  ├── composables/
  ├── stores/
  └── utils/

A backward-compatible alternative keeps the entry file outside the directory:

src/pages/modules/PcfProcess.vue
src/pages/modules/PcfProcess/
  ├── PcfProcess.routes.ts   ← optional nested route manifest
  ├── components/
  ├── composables/
  ├── stores/
  └── utils/

Best practices:

  • Prefer the index.vue convention for new modules

  • The directory name and top-level .vue file should match (when using the legacy layout)

  • The top-level file acts as the module entry point

  • Internal views/components are imported from the module directory

Automatic Route Generation#

Module routes are generated dynamically at startup. The router scans for both top-level .vue files and index.vue files inside module directories:

const moduleFileRecords = import.meta.glob('@/pages/modules/*.vue');

For each discovered module:

  • The file or directory name becomes the route name and base path

  • The component is lazy-loaded

  • Route metadata is derived from defineOptions

  • If a route manifest exists (routes.ts or .routes.ts), its child routes are registered automatically

Example generated route:

/modules/pcfprocess

Hint

No manual router configuration is required — this includes nested routes defined via a route manifest.

Module Options#

Modules declare metadata using defineOptions:

defineOptions({
  inheritAttrs: false,
  isDesktopModule: true,
  isMobileModule: true,
  isVisibleModule: true,
  isOnlyVisibleWithSelectedAas: false,
  isOnlyVisibleWithSelectedNode: false,
  moduleTitle: 'PCF Process',
});

Option reference#

Option

Description

moduleTitle

Display name in the Modules menu (defaults to file name)

isDesktopModule

Visible in desktop layout (default: true)

isMobileModule

Visible in mobile layout (default: false)

isVisibleModule

Controls menu visibility (route still accessible)

isOnlyVisibleWithSelectedAas

Only visible when an AAS is selected

isOnlyVisibleWithSelectedNode

Only visible when a Submodel or SME is selected

preserveRouteQuery

Preserve aas/path query parameters in the route

Note

If isOnlyVisibleWithSelectedAas or isOnlyVisibleWithSelectedNode is set, preserveRouteQuery is enabled automatically.

Hotkeys#

Modules can also define their own hotkeys. Hotkeys are a way to provide keyboard shortcuts for actions within the module.

In order to define hotkeys for a module, it is recommended to create a second <script> block (without setup) in the module’s main Vue file. This block should import and use the type PageShortcutDefinitions from the useRouteShortcuts composable.

The following example showcases how to define a hotkey that clears an asset ID input field when the user presses Cmd+Shift+Backspace:

<script lang="ts">
    import type { PageShortcutDefinitions } from '@/composables/Shortcuts/useRouteShortcuts';

    // Module shortcuts definition - available when this module is active
    export const shortcuts: PageShortcutDefinitions = () => [
        {
            id: 'my-module-clear-asset-id',
            title: 'Clear Asset ID',
            description: 'Clear the asset ID input field',
            prependIcon: 'mdi-eraser',
            category: 'My Module Shortcuts',
            keys: 'cmd+shift+backspace',
            handler: (event: KeyboardEvent) => {
                event.preventDefault();
                event.stopPropagation();
                const assetIdInput = document.querySelector('#asset-id-input input') as HTMLInputElement;
                if (assetIdInput) {
                    assetIdInput.value = '';
                    assetIdInput.dispatchEvent(new Event('input', { bubbles: true }));
                }
            },
        },
    ];
</script>

Hint

For more details on defining hotkeys, refer to the Defining Hotkeys section.

Modules in the Main Menu#

The main menu contains three tabs:

  • AAS - AAS related pages

  • Submodels - Submodel and SME related pages

  • Modules - Custom modules

All registered modules appear in the Modules tab.

../../../_images/module_menu.png

Modules tab in the main menu.#

Menu behavior:

  • Modules are filtered based on mobile/desktop mode

  • Visibility depends on AAS / node selection

  • Modules are sorted alphabetically by name

Mobile vs Desktop Modules#

The application distinguishes between mobile and desktop layouts using responsive breakpoints.

Use isDesktopModule and isMobileModule to control where a module appears.

Typical patterns:

  • Complex workflows: desktop-only

  • Viewer-style dashboards: desktop + mobile

Accessing Context and State#

Selected AAS and Node#

Modules should access the current selection via the AAS Store:

import { useAASStore } from '@/store/AASDataStore';

const aasStore = useAASStore();

const selectedAas = aasStore.getSelectedAAS;
const selectedNode = aasStore.getSelectedNode;

If preserveRouteQuery is enabled, the following are also available via the router:

  • aas query parameter

  • path query parameter

This allows reloading the page without losing context.

Loading and Modifying AAS Data#

Modules should reuse the same composables as core pages and plugins:

  • useAASHandling

  • useSMHandling

  • Client composables for repositories and registries

Warning

Direct API calls to AAS backend services are discouraged.

Routing Inside Modules#

Each module has a base route:

/modules/<module-name>

Nested Routes via Route Manifest#

Modules can define nested child routes through a dedicated route manifest file. This is the recommended approach for modules that require their own internal navigation (e.g. multi-step workflows, tabbed interfaces, or detail views).

Depending on the module layout, the manifest is placed as follows:

Module layout

Entry file

Route manifest

Directory with index.vue (recommended)

src/pages/modules/MyModule/index.vue

src/pages/modules/MyModule/routes.ts

Flat file (legacy)

src/pages/modules/MyModule.vue

src/pages/modules/MyModule.routes.ts

A route manifest exports a default object with a children array:

// src/pages/modules/MyModule/routes.ts
export default {
    children: [
        {
            path: 'technical-data',
            name: 'TechnicalData',
            component: () => import('@/pages/modules/MyModule/TechnicalData.vue'),
        },
    ],
};

The example above produces the route /modules/mymodule/technical-data.

Rules for nested routes#

  • Child paths must be relative (no leading /) and stay inside /modules/<module-name>/**.

  • Child route names are automatically prefixed with <ModuleName>__ to avoid collisions (e.g. MyModule__TechnicalData).

  • Child routes inherit module visibility, platform, and query metadata by default.

  • Existing modules without a route manifest continue to work unchanged.

Hint

The module entry component (index.vue / MyModule.vue) must include a <router-view /> element for child routes to render.

Alternative approaches#

For simpler internal navigation that does not require distinct URLs, modules can also use:

  • Internal tabs or step components

  • Navigation drawers

  • Conditional rendering

Deep linking via nested routes is encouraged but not required.

Layout Guidelines#

Modules are rendered inside the standard application shell:

  • App bar and menus are provided by the host application

  • The module controls the main content area

Recommended layout pattern:

<v-container fluid>
  <!-- module content -->
</v-container>
../../../_images/module_layout.png

Typical module layout inside the application shell.#

Feature Flags and Security#

Modules must respect global feature flags:

  • ALLOW_EDITING

  • ALLOW_UPLOADING

  • related environment flags

These flags are available via the Environment Store.

Warning

Accessing the Infrastructure Store directly from modules should be avoided unless absolutely necessary.

Some modules (e.g. import/export workflows) may intentionally interact with multiple infrastructures, but this should be done explicitly and with care.

Testing Modules#

There is currently no strict testing requirement for modules.

Recommended approach:

  • Move complex logic into composables or utils

  • Test those units as described in Testing & Quality Assurance

  • Use manual testing for UI-heavy workflows

Existing Modules as Reference#

The following modules demonstrate common patterns:

  • Test – minimal module structure

  • PcfProcess – dedicated module directory, AAS & SM handling

  • AasImporter – infrastructure access and data transfer

  • QueryLanguage – client composables usage

  • TestPreserveUrlQuery – route query preservation

Hint

Studying existing modules is often the fastest way to understand best practices.

Summary#

Modules provide a powerful extension mechanism for the BaSyx AAS Web UI:

  • Automatic routing and menu integration

  • Full-screen application features

  • Access to shared state, composables, and infrastructure

  • Clear separation from submodel-level plugins

When a feature grows beyond a single semantic visualization, a module is usually the right architectural choice.