npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

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-objects

Import 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 compare
  • objectB: 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 null and undefined correctly
  • 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.