ngx-st-diff-objects
v18.0.1
Published
Angular component for visualizing differences between two objects with color-coded highlighting.
Downloads
151
Readme
NgxStDiffObjects
Angular component for visualizing differences between two objects with color-coded highlighting.
Table of Contents
Overview
The ngx-st-diff-objects library provides components to compare two objects and display their differences visually. Perfect for:
- Audit logs and change history
- Before/after comparisons
- Form change detection
- Version comparison
- Data validation displays
Installation
npm install ngx-st-diff-objectsImport the module in your application:
import { NgxStDiffObjectsModule } from 'ngx-st-diff-objects';
@NgModule({
imports: [
NgxStDiffObjectsModule
]
})
export class AppModule { }Components
StDiffObjects
Main component that compares two objects and displays differences.
Selector
<st-diff-objects></st-diff-objects>Inputs
objectA
- Type:
any - Description: First object to compare (usually the "before" or "old" version).
- Example:
[objectA]="originalUser"
objectB
- Type:
any - Description: Second object to compare (usually the "after" or "new" version).
- Example:
[objectB]="updatedUser"
Features
Visual Highlighting:
- Deleted properties (in objectA but not in objectB) shown in red
- Added properties (in objectB but not in objectA) shown in green
- Changed properties shown with both old and new values
- Unchanged properties hidden by default
Deep Comparison: Recursively compares nested objects
Type Detection: Handles strings, numbers, booleans, dates, arrays, and nested objects
Same Object Detection: Shows message when objects are identical
Example
<st-diff-objects
[objectA]="userBeforeEdit"
[objectB]="userAfterEdit">
</st-diff-objects>export class ComparisonComponent {
userBeforeEdit = {
name: 'John Doe',
email: '[email protected]',
age: 30,
role: 'user'
};
userAfterEdit = {
name: 'John Doe',
email: '[email protected]',
age: 31,
role: 'admin',
phone: '+1234567890' // New field
};
}StDiffObjectDetails
Detailed comparison component showing field-by-field differences.
Selector
<st-diff-object-details></st-diff-object-details>Used internally by StDiffObjects but can be used standalone for custom layouts.
Service
StDiffObjectsService
Service providing the diff calculation logic.
Methods
diff(objectA: any, objectB: any): IDiffResponse
Compares two objects and returns a detailed difference report.
Parameters:
objectA: First object to compareobjectB: Second object to compare
Returns: IDiffResponse object containing:
interface IDiffResponse {
type: 'changed' | 'unchanged' | 'added' | 'deleted';
data: any;
value: {
[key: string]: IDiffResponse;
};
}Example:
import { StDiffObjectsService } from 'ngx-st-diff-objects';
export class MyComponent {
compareUsers() {
const diff = StDiffObjectsService.diff(
this.originalUser,
this.updatedUser
);
console.log('Diff type:', diff.type);
console.log('Changed fields:', Object.keys(diff.value));
// Check if specific field changed
if (diff.value['email']?.type === 'changed') {
console.log('Email was updated');
}
}
}Examples
Basic Difference Display
<div class="comparison-container">
<h3>User Profile Changes</h3>
<st-diff-objects
[objectA]="originalData"
[objectB]="modifiedData">
</st-diff-objects>
</div>export class UserChangesComponent {
originalData = {
firstName: 'John',
lastName: 'Doe',
email: '[email protected]',
active: true
};
modifiedData = {
firstName: 'John',
lastName: 'Smith', // Changed
email: '[email protected]', // Changed
active: true
};
}Audit Log / Change History
<div class="audit-log">
<h2>Change History</h2>
<mat-expansion-panel *ngFor="let change of changeHistory">
<mat-expansion-panel-header>
<mat-panel-title>
{{ change.timestamp | date:'medium' }} - {{ change.user }}
</mat-panel-title>
<mat-panel-description>
{{ change.description }}
</mat-panel-description>
</mat-expansion-panel-header>
<st-diff-objects
[objectA]="change.before"
[objectB]="change.after">
</st-diff-objects>
</mat-expansion-panel>
</div>export class AuditLogComponent implements OnInit {
changeHistory: ChangeRecord[] = [];
ngOnInit() {
this.auditService.getChangeHistory(this.entityId).subscribe(history => {
this.changeHistory = history;
});
}
}
interface ChangeRecord {
timestamp: Date;
user: string;
description: string;
before: any;
after: any;
}Form Before/After Comparison
<div class="form-comparison">
<div class="comparison-header">
<h3>Review Your Changes</h3>
<p>Please review the changes before saving</p>
</div>
<st-diff-objects
[objectA]="formInitialValue"
[objectB]="formCurrentValue">
</st-diff-objects>
<div class="actions">
<button mat-button (click)="cancel()">Cancel</button>
<button mat-raised-button color="primary" (click)="save()"
[disabled]="!hasChanges()">
Save Changes
</button>
</div>
</div>export class FormComparisonComponent {
formInitialValue: any;
formCurrentValue: any;
form: FormGroup;
constructor(private fb: FormBuilder) {
this.form = this.fb.group({
name: [''],
email: [''],
phone: ['']
});
}
ngOnInit() {
this.userService.getUser(this.userId).subscribe(user => {
this.formInitialValue = { ...user };
this.form.patchValue(user);
this.form.valueChanges.subscribe(value => {
this.formCurrentValue = value;
});
});
}
hasChanges(): boolean {
return JSON.stringify(this.formInitialValue) !==
JSON.stringify(this.formCurrentValue);
}
save() {
this.userService.updateUser(this.formCurrentValue).subscribe();
}
cancel() {
this.form.patchValue(this.formInitialValue);
}
}Version Comparison
<div class="version-comparison">
<h2>Document Version Comparison</h2>
<div class="version-selector">
<mat-form-field>
<mat-label>Version A</mat-label>
<mat-select [(value)]="versionA" (selectionChange)="loadVersions()">
<mat-option *ngFor="let v of versions" [value]="v.id">
Version {{ v.version }} - {{ v.date | date:'short' }}
</mat-option>
</mat-select>
</mat-form-field>
<mat-icon>compare_arrows</mat-icon>
<mat-form-field>
<mat-label>Version B</mat-label>
<mat-select [(value)]="versionB" (selectionChange)="loadVersions()">
<mat-option *ngFor="let v of versions" [value]="v.id">
Version {{ v.version }} - {{ v.date | date:'short' }}
</mat-option>
</mat-select>
</mat-form-field>
</div>
<st-diff-objects
*ngIf="versionAData && versionBData"
[objectA]="versionAData"
[objectB]="versionBData">
</st-diff-objects>
</div>export class VersionComparisonComponent {
versions: DocumentVersion[] = [];
versionA: string;
versionB: string;
versionAData: any;
versionBData: any;
ngOnInit() {
this.documentService.getVersions(this.documentId).subscribe(versions => {
this.versions = versions;
if (versions.length >= 2) {
this.versionA = versions[versions.length - 2].id;
this.versionB = versions[versions.length - 1].id;
this.loadVersions();
}
});
}
loadVersions() {
if (this.versionA && this.versionB) {
forkJoin({
a: this.documentService.getVersion(this.versionA),
b: this.documentService.getVersion(this.versionB)
}).subscribe(result => {
this.versionAData = result.a.data;
this.versionBData = result.b.data;
});
}
}
}Programmatic Difference Detection
import { StDiffObjectsService } from 'ngx-st-diff-objects';
export class DiffDetectionComponent {
checkForChanges() {
const original = {
name: 'Product A',
price: 100,
stock: 50,
categories: ['electronics', 'gadgets']
};
const modified = {
name: 'Product A',
price: 120, // Changed
stock: 50,
categories: ['electronics', 'gadgets', 'new'] // Changed
};
const diff = StDiffObjectsService.diff(original, modified);
// Check overall change status
if (diff.type === 'unchanged') {
console.log('No changes detected');
return;
}
// Get list of changed fields
const changedFields = Object.keys(diff.value).filter(
key => diff.value[key].type !== 'unchanged'
);
console.log('Changed fields:', changedFields);
// Process specific field changes
if (diff.value['price']?.type === 'changed') {
const oldPrice = diff.value['price'].data;
const newPrice = modified.price;
console.log(`Price changed from ${oldPrice} to ${newPrice}`);
// Trigger price change notification
this.notifyPriceChange(oldPrice, newPrice);
}
// Check for additions
if (diff.value['newField']?.type === 'added') {
console.log('New field added:', diff.value['newField'].data);
}
// Check for deletions
if (diff.value['removedField']?.type === 'deleted') {
console.log('Field removed:', diff.value['removedField'].data);
}
}
notifyPriceChange(oldPrice: number, newPrice: number) {
// Send notification, log change, etc.
}
}Nested Object Comparison
<st-diff-objects
[objectA]="originalProduct"
[objectB]="updatedProduct">
</st-diff-objects>originalProduct = {
id: 1,
name: 'Laptop',
specifications: {
cpu: 'Intel i5',
ram: '8GB',
storage: '256GB SSD'
},
pricing: {
cost: 500,
retail: 800,
discount: 0
}
};
updatedProduct = {
id: 1,
name: 'Laptop Pro', // Changed
specifications: {
cpu: 'Intel i7', // Changed
ram: '16GB', // Changed
storage: '512GB SSD' // Changed
},
pricing: {
cost: 500,
retail: 900, // Changed
discount: 10 // Changed
}
};Conditional Display
<div class="change-summary">
<div *ngIf="hasChanges(); else noChanges">
<mat-card>
<mat-card-header>
<mat-card-title>Pending Changes</mat-card-title>
</mat-card-header>
<mat-card-content>
<st-diff-objects
[objectA]="originalData"
[objectB]="currentData">
</st-diff-objects>
</mat-card-content>
<mat-card-actions>
<button mat-button (click)="discard()">Discard Changes</button>
<button mat-raised-button color="primary" (click)="apply()">
Apply Changes
</button>
</mat-card-actions>
</mat-card>
</div>
<ng-template #noChanges>
<p class="no-changes">No changes detected</p>
</ng-template>
</div>hasChanges(): boolean {
const diff = StDiffObjectsService.diff(
this.originalData,
this.currentData
);
return diff.type !== 'unchanged';
}Features
Visual Indicators
- Red (Deleted): Properties that existed in objectA but not in objectB
- Green (Added): Properties that exist in objectB but not in objectA
- Yellow (Changed): Properties with different values
- Nested Objects: Recursively compares and displays nested structures
- Arrays: Compares array contents
Display Format
Property Name
Old Value: [value from objectA]
New Value: [value from objectB]Smart Comparison
- Handles
nullandundefinedcorrectly - Compares dates by value
- Deep comparison of nested objects
- Array comparison
Build
Run ng build ngx-st-diff-objects to build the project. The build artifacts will be stored in the dist/ directory.
Publishing
After building your library with ng build ngx-st-diff-objects, go to the dist folder cd dist/ngx-st-diff-objects and run npm publish.
