Skip to content

Angular Explorer Packages

These packages comprise the MJExplorer application, MemberJunction’s primary Angular-based UI for browsing, editing, and managing data. They are published individually under the @memberjunction npm scope and consumed together by the MJExplorer host application.

Foundational packages that provide the application shell, routing, authentication, shared utilities, and module bundling.

PackagenpmDescription
explorer-app@memberjunction/ng-explorer-appComplete branded entry point for Explorer-style applications
explorer-core@memberjunction/ng-explorer-coreCore Explorer framework: application shell, routing, resource containers, and navigation
explorer-modules@memberjunction/ng-explorer-modulesConsolidated Explorer NgModule bundle that re-exports all Explorer feature modules
base-application@memberjunction/ng-base-applicationBaseApplication class system for app-centric navigation
auth-services@memberjunction/ng-auth-servicesAuthentication services with Auth0, MSAL, and Okta provider support
shared@memberjunction/ng-sharedShared Explorer utilities, base components, services, and events used across Explorer packages
workspace-initializer@memberjunction/ng-workspace-initializerWorkspace initialization service and components for bootstrapping the Explorer environment

Components for rendering, editing, and managing entity records through metadata-driven forms.

PackagenpmDescription
base-forms@memberjunction/ng-base-formsBase form components, field rendering, and validation framework
core-entity-forms@memberjunction/ng-core-entity-formsAuto-generated and custom entity forms with dynamic form loading and registration
entity-form-dialog@memberjunction/ng-entity-form-dialogModal dialog for displaying and editing any entity record

Grid and list components for browsing and managing collections of entity records.

PackagenpmDescription
list-detail-grid@memberjunction/ng-list-detail-gridMaster-detail grid for displaying dynamic and saved list details
simple-record-list@memberjunction/ng-simple-record-listLightweight component for displaying, editing, creating, and deleting records in any entity

Dashboard components for administrative and analytical views.

PackagenpmDescription
dashboards@memberjunction/ng-dashboardsDashboard components including AI model management, Entity Admin ERD, and Actions configuration

Supporting components for linking, permissions, settings, and change tracking.

PackagenpmDescription
link-directives@memberjunction/ng-link-directivesDirectives for turning elements into email, web, or record links
entity-permissions@memberjunction/ng-entity-permissionsComponents for displaying and editing entity-level permissions
explorer-settings@memberjunction/ng-explorer-settingsReusable components for the Explorer settings section
record-changes@memberjunction/ng-record-changesChange-tracking dialog with diff visualization for individual records

MJExplorer uses a fully metadata-driven architecture where routes, navigation, and component loading are resolved at runtime rather than statically declared. This enables unlimited custom dashboards and resources without changing core code.

Routes are not statically mapped to components. Instead, a ResourceResolver intercepts URL patterns and converts them into tab requests:

  1. URL like /app/Admin/AI%20Models is activated
  2. ResourceResolver reads the Application entity’s DefaultNavItems JSON from the database
  3. Finds the matching nav item by label, which specifies a DriverClass string (e.g. "AIModelsResource") and ResourceType (e.g. "Custom")
  4. Calls TabService.OpenTab() with that configuration
  5. ResourceContainerComponent renders the tab content (see next section)

This means Angular has no compile-time knowledge of which components map to which routes. Everything is resolved through metadata and the ClassFactory.

Every dashboard and resource component registers itself with MJGlobal’s ClassFactory via the @RegisterClass decorator:

@RegisterClass(BaseResourceComponent, 'AIModelsResource')
export class ModelManagementComponent extends BaseResourceComponent { }

When a tab becomes visible, ResourceContainerComponent does:

  1. ClassFactory.GetRegistrationAsync(BaseResourceComponent, 'AIModelsResource') — async lookup that triggers lazy loading if needed
  2. viewContainerRef.createComponent(registeredClass) — Angular’s dynamic component API instantiates it
  3. Wires up Data input and event callbacks

Not all components are in the initial bundle. Lazy loading is handled universally by ClassFactory itself — no consumer-specific retry logic needed:

  1. ClassFactory.GetRegistrationAsync() tries sync lookup first
  2. If null, calls registered lazy loaders with ('BaseResourceComponent', 'AIModelsResource')
  3. LazyModuleRegistry builds the compound key 'BaseResourceComponent::AIModelsResource' and looks it up in LAZY_FEATURE_CONFIG
  4. The config maps to a dynamic import: import('@memberjunction/ng-dashboards/ai-dashboards.module')
  5. The chunk loads, @RegisterClass decorators execute — ClassFactory now has the class
  6. Retry succeeds, component renders normally

This works for ALL base classes (BaseResourceComponent, BaseDashboard, BaseApplication, etc.), not just resource components. Any consumer using CreateInstanceAsync or GetRegistrationAsync gets lazy loading automatically.

See guides/LAZY_LOADING_GUIDE.md for the complete guide.

Modern bundlers (ESBuild, Vite) cannot detect that ClassFactory.CreateInstance('AIModelsResource') needs a specific class — there’s no static import path. Without intervention, the bundler tree-shakes these classes as dead code.

MemberJunction solves this with class registration manifests generated by mj codegen manifest. The generator walks the dependency tree, finds every @RegisterClass-decorated class via TypeScript AST parsing, and emits a file with explicit named imports:

import { ModelManagementComponent } from '@memberjunction/ng-dashboards';
import { PromptManagementComponent } from '@memberjunction/ng-dashboards';
// ... hundreds more
export const CLASS_REGISTRATIONS = [
ModelManagementComponent,
PromptManagementComponent,
// ...
];

The bundler sees named imports referenced in an exported array — it cannot tree-shake them.

Dual-manifest architecture:

  • Pre-built manifests ship inside bootstrap packages (@memberjunction/ng-bootstrap, @memberjunction/ng-bootstrap-lite). Generated at MJ release time, they cover all @memberjunction/* classes.
  • Supplemental manifests are generated locally at prestart/prebuild with --exclude-packages @memberjunction to capture only user-defined custom classes.

For lazy-loaded modules, ng-bootstrap-lite intentionally excludes dashboard and settings packages. Those classes aren’t in the initial manifest (so they’re not in the initial bundle). They get pulled in via dynamic import() when the user navigates to them.

See /packages/CodeGenLib/CLASS_MANIFEST_GUIDE.md for comprehensive manifest documentation.

FileRole
explorer-core/.../app-routing.module.tsRoute definitions and ResourceResolver
explorer-core/.../resource-container-component.tsDynamic component loading with lazy fallback
explorer-core/.../services/lazy-module-registry.tsManages lazy chunk loading and deduplication
explorer-core/.../services/lazy-feature-config.tsMaps 60+ resource types to dynamic import functions
dashboards/package.json (exports field)Subpath exports enabling ESBuild code splitting
@memberjunction/ng-bootstrap-litePre-built manifest excluding lazy-loaded packages