@memberjunction/ng-compare-records v2.48.0
@memberjunction/ng-compare-records
The @memberjunction/ng-compare-records package provides a powerful Angular component for comparing multiple records of the same entity type. It displays records side-by-side in a grid format, highlighting differences between them, and optionally allows selecting values from different records to create a composite record.
Features
- Side-by-side comparison of multiple entity records
- Option to show only differences between records
- Option to suppress fields with blank values
- Highlighting of selected record and field values
- "Selection mode" that allows picking values from different records
- Automatic record dependency detection
- Support for composite primary keys
- Responsive grid with virtual scrolling
- Field formatting according to entity metadata
- Automatic data loading for incomplete records
- Read-only field indication with italic styling
- Header click to select base record in selection mode
Installation
npm install @memberjunction/ng-compare-recordsRequirements
- Angular 18+
- @memberjunction/core
- @memberjunction/core-entities
- @progress/kendo-angular-grid
- @progress/kendo-angular-inputs
- @progress/kendo-angular-label
Usage
Basic Setup
First, import the CompareRecordsModule in your module:
import { CompareRecordsModule } from '@memberjunction/ng-compare-records';
@NgModule({
imports: [
// other imports...
CompareRecordsModule
],
})
export class YourModule { }Basic Usage
Use the component in your template for simple record comparison:
<mj-compare-records
[recordsToCompare]="recordsToCompare"
[entityName]="entityName">
</mj-compare-records>In your component:
import { Component, OnInit } from '@angular/core';
import { BaseEntity, Metadata } from '@memberjunction/core';
@Component({
selector: 'app-compare-example',
templateUrl: './compare-example.component.html',
})
export class CompareExampleComponent implements OnInit {
recordsToCompare: BaseEntity[] = [];
entityName: string = 'Customer';
constructor(private metadata: Metadata) {}
async ngOnInit() {
// Load records to compare
const customer1 = await this.metadata.GetEntityObject('Customer', 1);
const customer2 = await this.metadata.GetEntityObject('Customer', 2);
const customer3 = await this.metadata.GetEntityObject('Customer', 3);
await Promise.all([
customer1.Load(),
customer2.Load(),
customer3.Load()
]);
this.recordsToCompare = [customer1, customer2, customer3];
}
}Advanced Usage with Selection Mode
When you need to allow users to select values from different records to create a composite record:
<mj-compare-records
[recordsToCompare]="recordsToCompare"
[entityName]="entityName"
[selectionMode]="true">
</mj-compare-records>In your component:
import { Component, OnInit, ViewChild } from '@angular/core';
import { BaseEntity, Metadata } from '@memberjunction/core';
import { CompareRecordsComponent } from '@memberjunction/ng-compare-records';
@Component({
selector: 'app-advanced-compare',
templateUrl: './advanced-compare.component.html',
})
export class AdvancedCompareComponent implements OnInit {
@ViewChild(CompareRecordsComponent) compareComponent!: CompareRecordsComponent;
recordsToCompare: BaseEntity[] = [];
entityName: string = 'Product';
constructor(private metadata: Metadata) {}
async ngOnInit() {
// Load records to compare
// ... Load your records
}
async createCompositeRecord() {
// Get the base record (selected record)
const baseRecord = this.recordsToCompare.find(r =>
r.PrimaryKey.Equals(this.compareComponent.selectedRecordCompositeKey)
);
if (baseRecord) {
// Create a new entity object
const newRecord = await this.metadata.GetEntityObject(this.entityName);
// Copy all values from the base record
for (const field of baseRecord.Fields) {
newRecord.Set(field.Name, baseRecord.Get(field.Name));
}
// Apply all overrides from the field map
for (const field of this.compareComponent.fieldMap) {
// Find the source record
const sourceRecord = this.recordsToCompare.find(r =>
r.PrimaryKey.Equals(field.CompositeKey)
);
if (sourceRecord) {
// Set the value from the source record
newRecord.Set(field.fieldName, sourceRecord.Get(field.fieldName));
}
}
// Use the new record
console.log('Composite record created:', newRecord);
// Save the record if needed
// await newRecord.Save();
}
}
}Working with Raw Data
The component can also work with raw data objects (not BaseEntity instances). It will automatically:
- Detect if records need additional fields from the database
- Load missing data efficiently using RunView
- Convert raw objects to BaseEntity instances
// You can pass raw objects with primary key values
const rawRecords = [
{ CustomerID: 1 },
{ CustomerID: 2 },
{ CustomerID: 3 }
];
// The component will automatically load full records
this.recordsToCompare = rawRecords;API Reference
CompareRecordsComponent
Inputs
| Name | Type | Default | Description |
|---|---|---|---|
recordsToCompare | BaseEntity[] | [] | Array of records to compare |
entityName | string | '' | Name of the entity type being compared |
visibleColumns | ViewColumnInfo[] | [] | Optional columns to display (defaults to all entity fields) |
selectionMode | boolean | false | Whether to enable selecting values from different records |
Properties
| Name | Type | Description |
|---|---|---|
selectedRecordCompositeKey | CompositeKey | The primary key of the currently selected record (in selection mode) |
fieldMap | {fieldName: string, CompositeKey: CompositeKey, value: any}[] | Maps fields to records other than the selected record (in selection mode) |
showDifferences | boolean | Whether to show only fields with different values |
suppressBlankFields | boolean | Whether to hide fields with blank values |
Methods
| Name | Return Type | Description |
|---|---|---|
prepareViewData() | Promise<void> | Refreshes the view with the current records |
ResizeGrid() | void | Manually resize the grid |
Styling
The component uses the following CSS classes that can be customized:
.dialog-container: Main container for the component.dialog-toolbar: Contains the checkboxes for options.compare-grid-rows: Applied to grid rows.cell: Basic cell styling.cell-not-selected: Applied to cells not selected in selection mode.cell-selected: Applied to cells from the selected record in selection mode.cell-selected-override: Applied to cells selected from non-selected records.cell-readonly: Applied to read-only fields
Building the Package
This package uses the Angular compiler (ngc) for building:
# From the package directory
npm run build
# From the repository root using turbo
turbo build --filter="@memberjunction/ng-compare-records"Dependencies
Peer Dependencies
- @angular/common: 18.0.2
- @angular/core: 18.0.2
- @angular/forms: 18.0.2
Runtime Dependencies
- @memberjunction/core: 2.43.0
- @memberjunction/core-entities: 2.43.0
- @progress/kendo-angular-grid: 16.2.0
- tslib: ^2.3.0
Dev Dependencies
- @angular/compiler: 18.0.2
- @angular/compiler-cli: 18.0.2
Integration with MemberJunction
This component is designed to work seamlessly with the MemberJunction framework:
- Entity Metadata: Uses MJ's metadata system to understand entity structure, field types, and relationships
- Field Formatting: Leverages EntityField.FormatValue() for consistent display of values
- Primary Key Support: Works with both single and composite primary keys using CompositeKey class
- Dependency Detection: Uses MJ's GetRecordDependencies() to determine the default selected record
- Data Access: Utilizes RunView for efficient batch loading of incomplete records
Advanced Features
Selection Mode Behavior
In selection mode:
- The record with the most dependencies is automatically selected as the base record
- Click on any cell to select that field's value from that record
- Click on column headers to change the base record
- Selected record header shows "✓✓✓" prefix
- Field values from the base record have a yellowgreen background
- Override values (from other records) have a lightpink background
- Read-only fields cannot be selected and appear in italic
Performance Optimizations
- Virtual Scrolling: Handles large datasets efficiently
- Batch Loading: Loads multiple incomplete records in a single database query
- Debounced Resizing: Window resize events are debounced to prevent excessive recalculation
Troubleshooting
Common Issues
- Records not displaying: Ensure entity name matches exactly (case-insensitive)
- Missing fields: Check that visibleColumns includes all desired fields
- Selection not working: Verify selectionMode is set to true
- Styling issues: Ensure Kendo UI theme is properly loaded in your application
9 months ago
5 months ago
9 months ago
6 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
6 months ago
6 months ago
5 months ago
9 months ago
9 months ago
9 months ago
6 months ago
9 months ago
9 months ago
9 months ago
9 months ago
9 months ago
5 months ago
8 months ago
8 months ago
8 months ago
7 months ago
7 months ago
7 months ago
9 months ago
5 months ago
9 months ago
9 months ago
9 months ago
9 months ago
8 months ago
7 months ago
5 months ago
9 months ago
9 months ago
5 months ago
5 months ago
8 months ago
8 months ago
7 months ago
9 months ago
9 months ago
5 months ago
5 months ago
8 months ago
8 months ago
6 months ago
6 months ago
10 months ago
5 months ago
8 months ago
5 months ago
10 months ago
6 months ago
11 months ago
10 months ago
11 months ago
6 months ago
11 months ago
5 months ago
8 months ago
8 months ago
12 months ago
6 months ago
6 months ago
9 months ago
12 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago