The @memberjunction/ng-core-entity-forms package provides a comprehensive set of Angular form components for all core MemberJunction entities. It includes both auto-generated form components based on entity metadata and custom form components with enhanced functionality for specific entities.
npm install @memberjunction/ng-core-entity-forms
The package includes two main modules:
CoreGeneratedFormsModule - Contains all auto-generated form componentsMemberJunctionCoreEntityFormsModule - Contains custom form componentsImport the necessary modules in your application module:
import {
CoreGeneratedFormsModule,
MemberJunctionCoreEntityFormsModule
} from '@memberjunction/ng-core-entity-forms';
@NgModule({
imports: [
// other imports...
CoreGeneratedFormsModule,
MemberJunctionCoreEntityFormsModule
],
})
export class YourModule { }
Generated forms are dynamically loaded based on entity names. The typical pattern is to use a component factory to create the appropriate form component:
import { Component, ViewChild, ViewContainerRef, ComponentFactoryResolver } from '@angular/core';
import { Metadata } from '@memberjunction/core';
@Component({
selector: 'app-entity-form-container',
template: '<ng-container #formContainer></ng-container>'
})
export class EntityFormContainerComponent {
@ViewChild('formContainer', { read: ViewContainerRef }) formContainer!: ViewContainerRef;
constructor(
private componentFactoryResolver: ComponentFactoryResolver,
private metadata: Metadata
) {}
async loadEntityForm(entityName: string, recordId: any) {
// Clear the container
this.formContainer.clear();
// Get the form component class for the entity
const formComponent = await this.metadata.GetEntityFormComponent(entityName);
if (formComponent) {
// Create the component
const factory = this.componentFactoryResolver.resolveComponentFactory(formComponent);
const componentRef = this.formContainer.createComponent(factory);
// Set inputs
componentRef.instance.recordId = recordId;
// Initialize the form
await componentRef.instance.ngOnInit();
}
}
}
Custom forms can be used directly in your templates:
<!-- For the extended Entity form -->
<mj-entities-form
[recordId]="entityId"
[showToolbar]="true"
(saved)="onEntitySaved($event)">
</mj-entities-form>
<!-- For the extended EntityAction form -->
<mj-entity-action-form
[recordId]="actionId"
[showToolbar]="true"
(saved)="onActionSaved($event)">
</mj-entity-action-form>
The CoreGeneratedFormsModule contains multiple sub-modules that collectively register all generated form components. Each entity has:
EntityFormComponent)EntityDetailsComponent, EntityTopComponent)LoadEntityFormComponent())The MemberJunctionCoreEntityFormsModule contains extended form components that provide additional functionality beyond the auto-generated forms:
EntityFormExtendedComponent - Enhanced form for Entity managementEntityActionExtendedFormComponent - Enhanced form for EntityAction managementActionTopComponentExtended - Custom top section for Action formsBefore reaching for a full custom form override, check whether the lighter-weight BaseFormPanel slot system covers your need.
BaseFormPanel slots (lightweight, additive)Standalone Angular components that extend BaseFormPanel and self-register via @RegisterClassEx(BaseFormPanel, { metadata: { entity, slot, sortKey } }). They mount into generated forms dynamically through <mj-form-panel-slot>. No *Extended class, no restating the generated layout. Panels live in src/lib/panels/{entity-folder}/ in this package (see panels/content-sources/ for the TagPipelineConfigurationPanel and WebsiteCrawlerSettingsPanel reference implementations).
When you'd reach for this:
Full authoring guide + slot positions + fallback chain: packages/Angular/Generic/base-forms/PANELS.md.
The classic pattern documented below. Use when the generated layout is the wrong starting point — you need to hide generated panels, restructure the toolbar, embed a fundamentally different UX (flow editor, Kanban board, wizard), or otherwise own the entire form's render. AIAgentFormComponentExtended is the canonical example: the flow editor isn't a "panel alongside fields," it IS the form.
In doubt: "generated form + extras" → Pattern 1. "generated form is the wrong shape entirely" → Pattern 2.
packages/Angular/Explorer/core-entity-forms/src/lib/custom/{EntityName}/{entity-name}-form.component.ts (main logic){entity-name}-form.component.html (template)TemplateFormComponent) which inherit from BaseFormComponent@RegisterClass(BaseFormComponent, 'EntityName') decoratorcustom-forms.module.ts declarations, exports, and import required Kendo modulesany - always use proper entity types (TemplateEntity, TemplateCategoryEntity)Metadata.GetEntityObject<EntityType>('EntityName') patternRunView with ResultType='entity_object' and generic typingTemplateEngineBase.Instance.TemplateContentTypes@if, @for, @switch instead of structural directivestrack in @for loops for performanceSaveRecord(StopEditModeAfterSave: boolean)super.SaveRecord()trim().toLowerCase() comparison for category namesMJNotificationService.Instance.CreateSimpleNotification() for user feedbackEditMode state, implement proper change trackingmjFillContainer directive with bottomMargin for proper container sizingmj-form-field for individual fields; avoid problematic mj-form-section propertiesnpm run build in specific package directory for TypeScript checkingnpm install in package directories - always at repo rootnpm install at rootsrc/shared/form-styles.css✅ Do:
❌ Avoid:
any typesMetadata.GetEntityObject()EditMode statenpm install in package directoriesEach entity form typically follows this structure:
BaseFormComponent| Component | Description |
|---|---|
| Entity forms | Forms for managing metadata entities (Entity, EntityField, etc.) |
| Action forms | Forms for managing actions and workflows |
| User forms | Forms for user management and permissions |
| Integration forms | Forms for external system integrations |
| AI-related forms | Forms for AI models, prompts, and agents |
| Content forms | Forms for content management |
| Communication forms | Forms for messaging and notifications |
@memberjunction/core package for field definitionsThe AI Agent Type UI Customization feature allows AI Agent Types to define custom form sections or complete form overrides in the AI Agent form UI. This enables different agent types (like Flow, Analysis, Support, etc.) to have specialized UI components tailored to their specific configuration needs.
The AIAgentType table includes three columns for UI customization (added in v2.76):
UIFormSectionKey (NVARCHAR(500) NULL) - Registration key for custom form section componentUIFormKey (NVARCHAR(500) NULL) - Registration key for complete form override componentUIFormSectionExpandedByDefault (BIT NOT NULL DEFAULT 1) - Whether custom section starts expandedThe AI Agent form (ai-agent-form.component.ts) implements dynamic loading:
UIFormSectionKey on the agent typeEditMode changes to the custom sectionCustom form sections must:
BaseFormSectionComponent@RegisterClass decorator with key pattern: AI Agents.{SectionKey}Example:
@RegisterClass(BaseFormSectionComponent, 'AI Agents.FlowAgentSection')
export class FlowAgentFormSectionComponent extends BaseFormSectionComponent {
// Implementation
}
The FlowAgentFormSectionComponent demonstrates a complete implementation:
Create the Component
@Component({
selector: 'mj-custom-agent-section',
template: `...`,
styles: [`...`]
})
@RegisterClass(BaseFormSectionComponent, 'AI Agents.CustomSection')
export class CustomAgentSectionComponent extends BaseFormSectionComponent {
// Access this.record for the current AIAgentEntity
// Use this.EditMode to determine read/write state
}
Register in Module
custom-forms.module.tspublic-api.ts if needed externallyUpdate Agent Type
UPDATE AIAgentType
SET UIFormSectionKey = 'CustomSection',
UIFormSectionExpandedByDefault = 1
WHERE Name = 'YourAgentType';
For complete form replacement (not just a section), use UIFormKey:
UPDATE AIAgentType
SET UIFormKey = 'CustomCompleteForm'
WHERE Name = 'YourAgentType';
The custom form component should extend BaseFormComponent instead.
AIAgentFormComponent (generated)
└── AIAgentFormComponentExtended (custom)
└── Dynamic Custom Section (via ViewContainerRef)
└── FlowAgentFormSectionComponent (or other custom section)
anyThe enhanced entity form provides comprehensive visualization of IS-A (inheritance) relationships between entities. This feature helps users understand type hierarchies and navigate entity inheritance chains.
When viewing an entity that participates in IS-A relationships, the form displays several visual elements:
Purple Hierarchy Badges: Entities with IS-A relationships display purple badges with hierarchy icons indicating their role:
Parent Entity Breadcrumb: For child entities, a breadcrumb navigation shows the complete inheritance chain from the root parent down to the current entity. Users can click any parent in the chain to navigate to that entity.
Field Source Indicators: Individual fields show which parent entity they originated from. This helps users understand where each field is defined in the inheritance hierarchy.
IS-A Hierarchy Visualization Panel: A dedicated panel displays the complete type tree, showing:
IS-A Settings Panel: A collapsible panel allows users to manage the IS-A relationship:
The IS-A visualization is built into the generated entity forms and automatically activates when an entity has IS-A relationships defined in the metadata. No additional configuration is required beyond setting up the IS-A relationships in the database schema.
For more information on IS-A relationships and implementation, see:
Virtual entities in MemberJunction are read-only entities backed by database views rather than physical tables. The entity forms provide specialized UI to clearly indicate virtual entity status and limitations.
Virtual entities are displayed with distinctive visual elements:
Purple Badge with Eye Icon: A prominent badge labeled "Virtual Entity (Read-Only)" appears at the top of the form, featuring an eye icon to indicate the view-based nature.
Underlying View Name: The form displays the name of the database view that powers the virtual entity, helping users understand the data source.
Disabled Actions: Virtual entities do not show Create, Edit, or Delete buttons, as these operations are not supported for read-only views.
Virtual entities behave identically to regular entities for read operations:
The key distinction is that mutation operations (Create, Update, Delete) are blocked at runtime, ensuring data integrity for view-based entities.
Virtual entities are commonly used for:
Virtual entities are defined by setting the IsVirtualEntity flag in the entity metadata. The form UI automatically detects this flag and applies the appropriate read-only restrictions and visual indicators.
For more information on virtual entities and implementation, see: