@memberjunction/ng-record-changes
Angular components for browsing and restoring a record’s change history. Renders the RecordChange timeline as a slide-in panel with type-aware diffs, version-label chips, restore lineage, and a reusable preview panel for the actual restore operation.
flowchart LR
subgraph Timeline["RecordChangesComponent"]
TL[Slide-in panel] --> CARDS[Date-grouped change cards]
CARDS --> DIFFS[Type-aware diffs<br/>boolean / date / number / text]
CARDS --> CHIP[Restored-from lineage chip]
FILTERS[Conditional filter pills] --> POP[Overflow popover<br/>when 3+ pills]
end
subgraph Restore["RestorePreviewPanelComponent"]
PV[Slide-in preview] --> ROWS[Field-by-field rows<br/>with checkboxes]
ROWS --> MODE{Mode}
MODE -->|live| DIFF[current vs snapshot]
MODE -->|undelete| SNAP[snapshot only]
ROWS --> COMMIT[BeforeRestoreCommit<br/>cancelable]
end
Timeline -- "user clicks Restore" --> Restore
Restore -- "RestoreConfirmed event<br/>(host applies + saves)" --> HOST[Host component]
style Timeline fill:#264FAF,stroke:#1e3f8c,color:#fff
style Restore fill:#7c3aed,stroke:#5b21b6,color:#fff
style HOST fill:#16a34a,stroke:#15803d,color:#fff
Installation
Section titled “Installation”npm install @memberjunction/ng-record-changesImport the Module
Section titled “Import the Module”import { RecordChangesModule } from '@memberjunction/ng-record-changes';
@NgModule({ imports: [RecordChangesModule]})export class MyModule { }Timeline + Restore (the typical case)
Section titled “Timeline + Restore (the typical case)”<mj-record-changes [record]="myEntity" [AllowRestore]="true" (dialogClosed)="showHistory = false" (RestoreRequested)="onRestoreRequested($event)"></mj-record-changes>async onRestoreRequested(event: RestoreVersionEvent) { // Apply each selected snapshot field for (const fv of event.FieldValues) { this.myEntity.Set(fv.FieldName, fv.Value); } // Mark the next save as a restore so the provider populates lineage columns this.myEntity.SetRestoreContext(event.SourceChangeID, event.Reason); try { await this.myEntity.Save(); } finally { this.myEntity.ClearRestoreContext(); }}The host is responsible for the actual save so consumers can intercept (custom approval, audit logging, etc.). record-form-container already wires this end-to-end.
Standalone Restore Preview (un-delete from a Recycle Bin)
Section titled “Standalone Restore Preview (un-delete from a Recycle Bin)”<mj-restore-preview-panel [Visible]="showPreview" [Mode]="'undelete'" [RecordChange]="deletedChange" [EntityName]="'Customers'" (RestoreConfirmed)="onUndelete($event)" (RestoreCancelled)="showPreview = false"></mj-restore-preview-panel>API Reference
Section titled “API Reference”RecordChangesComponent (mj-record-changes)
Section titled “RecordChangesComponent (mj-record-changes)”The slide-in timeline of all changes to a single record. Hosts the reusable RestorePreviewPanelComponent for the actual restore confirmation flow.
Inputs
Section titled “Inputs”| Input | Type | Default | Description |
|---|---|---|---|
record | BaseEntity | — | Required. The live record whose change history will be displayed. |
AllowRestore | boolean | false | When true, renders a “Restore record to this version” button on each change card and exposes the RestoreRequested event. |
Outputs
Section titled “Outputs”| Output | Event Type | Description |
|---|---|---|
dialogClosed | void | Emitted when the user closes the slide-in. |
RestoreRequested | RestoreVersionEvent | Emitted after the user confirms a restore in the preview panel. The host is responsible for applying the snapshot and saving. |
RestoreVersionEvent
Section titled “RestoreVersionEvent”| Property | Type | Description |
|---|---|---|
SourceChangeID | string | ID of the historical RecordChange row whose state is being restored. Pass to BaseEntity.SetRestoreContext(). |
ChangedAt | Date | When the historical change was made. |
ChangedByUser | string | Display name / email of who made the historical change. |
Reason | string | null | Optional user-entered reason. Pass to BaseEntity.SetRestoreContext(). |
FieldValues | Array<{ FieldName; Value }> | Selected snapshot field values, ready to pass to BaseEntity.Set(). |
Conditional filter pills
Section titled “Conditional filter pills”The filter bar renders only chips for change types/sources that actually exist in the loaded data — empty types never show. When more than two conditional chips would render they collapse into a “More filters ▾” popover with checkboxes. The “All” chip is always present.
Lineage chip
Section titled “Lineage chip”Change rows where RestoredFromID is populated render a violet “Restored from {time} by {user}” chip. Clicking the chip scrolls to and highlights the source row in the same timeline.
RestorePreviewPanelComponent (mj-restore-preview-panel)
Section titled “RestorePreviewPanelComponent (mj-restore-preview-panel)”Reusable slide-in that previews a restore operation against a historical MJRecordChangeEntity and lets the user confirm with field-level granularity. Used by both RecordChangesComponent (for live-record restores) and RecycleBinComponent (for un-delete inserts).
Inputs
Section titled “Inputs”| Input | Type | Default | Description |
|---|---|---|---|
Visible | boolean | false | Controls panel visibility. |
Mode | 'live' | 'undelete' | 'live' | live shows current-vs-snapshot diff; undelete shows snapshot only (the live record no longer exists). |
RecordChange | MJRecordChangeEntity | null | Required. The historical change row whose state will be restored. The component reads FullRecordJSON to determine the target state. Any Type (Create, Update, Delete, Snapshot) is a valid restore source. |
LiveRecord | BaseEntity | null | null | The current live record to diff against. Required in live mode, ignored in undelete mode. |
EntityName | string | null | null | Required in undelete mode (where there’s no LiveRecord to read it from). Optional in live mode. |
RequireReason | boolean | false | When true, the Restore button disables until the user enters a non-empty reason. |
HideReason | boolean | false | When true, hides the optional reason text area entirely. |
Outputs
Section titled “Outputs”| Output | Event Type | Description |
|---|---|---|
BeforeRestoreCommit | BeforeRestoreCommitEvent | Cancelable. Fires when the user clicks Restore but before RestoreConfirmed. Set cancel = true to abort. |
RestoreConfirmed | RestoreCommitEvent | Fires after the user confirms (and BeforeRestoreCommit was not cancelled). Host applies the field values and saves. |
RestoreCancelled | void | Fires when the user dismisses the preview without restoring. |
RestoreCommitEvent
Section titled “RestoreCommitEvent”| Property | Type | Description |
|---|---|---|
SourceChangeID | string | ID of the source RecordChange row. |
Reason | string | null | Optional user-entered reason. |
FieldValues | Array<{ FieldName; Value }> | Selected field values, ready for BaseEntity.Set(). |
AllRows | RestoreFieldRow[] | Full preview rows including unselected, for audit/logging. |
Mode | 'live' | 'undelete' | The mode the panel was operating in. |
Semantic correctness
Section titled “Semantic correctness”The preview compares the full snapshot captured in the source change’s FullRecordJSON to the current live record (or to nothing in undelete mode). It does NOT roll back a single delta — restoring v2 means “make the record look like it did at v2”, not “undo v3’s changes”.
This also means any change row is a valid restore target: Create (the original state), Update (post-update state), Snapshot (an explicit point-in-time capture from the Version Label system), or Delete (state before deletion — used by the Recycle Bin).
Type Exports
Section titled “Type Exports”import { // Timeline RecordChangesComponent, RecordChangesModule, RestoreVersionEvent, FieldChangeInfo, DateGroup, FilterPill,
// Restore Preview RestorePreviewPanelComponent, RestorePreviewMode, RestoreFieldRow, RestoreCommitEvent, BeforeRestoreCommitEvent,} from '@memberjunction/ng-record-changes';Dependencies
Section titled “Dependencies”Runtime Dependencies
Section titled “Runtime Dependencies”| Package | Description |
|---|---|
@memberjunction/core | Core framework (BaseEntity, Metadata, RunView) |
@memberjunction/core-entities | MJRecordChangeEntity definition |
@memberjunction/ng-shared-generic | Shared generic components (mj-loading) |
@memberjunction/ng-ui-components | Provides mj-slide-panel |
@memberjunction/ng-versions | Version label create wizard |
@memberjunction/ng-notifications | Toast notifications |
diff | Word/character text diff for the timeline |
Peer Dependencies
Section titled “Peer Dependencies”@angular/common^21.x@angular/core^21.x@angular/forms^21.x
cd packages/Angular/Generic/record-changesnpm run buildLicense
Section titled “License”ISC