Full Trust European Hosting

BLOG about Full Trust Hosting and Its Technology - Dedicated to European Windows Hosting Customer

AngularJS Hosting Europe - HostForLIFE :: Signals in Angular: Deep Drive

clock February 27, 2026 06:37 by author Peter

Angular has relied on its Change Detection mechanism and Observables for years to manage reactivity. A more precise, reliable, and effective method of handling state updates was made possible with the introduction of Signals, a new reactive primitive with Angular 16. Signals represent a major advancement in Angular's reactivity model, with the goal of streamlining state management while enhancing rendering speed and application effectiveness in general.

Signals: What Are They?
When a value changes, a signal, which is a reactive container, automatically alerts its dependents. Signals function synchronously and offer more transparent state tracking than Observables, which are push-based and usually asynchronous.

Key Characteristics

  • Lightweight: They introduce very little overhead during creation and updates.
  • Fine-grained: Only the components or computations that depend on the changed value are updated, rather than triggering a full view refresh.
  • Predictable: State changes propagate immediately and in a consistent manner.
  • Compatible: They integrate smoothly with existing Angular features and APIs.

Why Angular Introduced Signals?

Before Signals, Angular’s reactivity model leaned on:

  • Zone.js-driven change detection
  • @Input() property bindings
  • RxJS Observables
  • Manual state management techniques

While these tools were powerful, they came with notable drawbacks:

  • Entire component trees could re-render unnecessarily
  • Debugging reactive flows was often complicated
  • Performance tuning demanded deep expertise in change detection strategies

Signals solve these challenges by introducing fine-grained reactivity. Instead of re-rendering broadly, Angular now updates only the parts of the application that directly depend on the changed state.

Signal Types
1. Signals That Can Be Written

Writable signals provide an API that lets you directly change their values. The signal function is called and the starting value is passed in as an argument to create them.Example:
Example:
import { signal } from '@angular/core';

const counter = signal(0);
counter.set(1); // updates value
counter.update(value => value + 1); // increments

Core Methods

Method NameDescription

set(newValue)

The set() method replaces the current value of the signal with a new one.

update(fn)

The update() method modifies the signal’s value based on its current state.

asReadonly()

The asReadonly() method creates a read-only version of the signal.

2. Computed Signals

Computed signals are reactive values that are automatically derived from one or more other signals. Instead of holding independent state, they act as pure functions of existing signals, recalculating only when their dependencies change.

Example:
import { signal, computed } from '@angular/core';

const price = signal(200);
const quantity = signal(4);

const total = computed(() => price() * quantity());

console.log(total()); // 800

If price or quantity changes, total is recalculated automatically. You don’t need to manually trigger updates—Angular handles the dependency tracking for you.

Important Behaviors

  • Lazy evaluation: Computed signals don’t run until they’re accessed.
  • Caching: Results are stored, so repeated reads don’t cause unnecessary recomputation.
  • Dependency awareness: They re-run only when one of their input signals changes.

Why They’re Useful?

  • Simplify derived state (e.g., totals, filters, formatted values).
  • Reduce boilerplate compared to manual subscriptions or RxJS operators.
  • Improve performance by avoiding redundant recalculations.
  • Make reactive flows more predictable and easier to debug.

3. Effects
Effects are reactive functions that automatically run whenever the signals they depend on change. They’re designed to handle side effects—operations that go beyond pure state updates.

Example:
import { effect } from '@angular/core';

effect(() => {
console.log('Total changed:', total());
});


In this example, whenever total() changes, the effect executes and logs the new value.

Common Use Cases

  • Effects are ideal for tasks that need to respond to state changes but don’t belong in core business logic:
  • Logging application state changes
  • Triggering API calls when certain values update
  • Interacting with the DOM (e.g., animations, focus management)
  • Synchronizing state with local storage or external systems

Best Practices

  • Keep effects focused on side effects only.
  • Avoid embedding business logic inside effects—this should remain in signals or computed signals.
  • Prevent circular dependencies (e.g., an effect updating a signal that triggers the same effect again).

Signals vs Observables

FeatureSignalsObservables
Nature Synchronous reactive value holder Asynchronous data stream
Data Flow Pull-based (value is read directly) Push-based (values are emitted to subscribers)
Primary Use Case Local UI state management Async operations (HTTP, events, streams)
Change Detection Fine-grained updates (only dependent parts re-render) Works with async pipe or manual subscription
Value Access Always holds a current value May or may not have a current value
Complexity Simple and lightweight API Powerful but involves operators and subscriptions
Subscription Required No Yes
Best For Component state and derived state Server calls, real-time data, event handling

Advanced Concepts

1. Reactive Contexts
Signals automatically track dependencies through reactive contexts. When a Signal is accessed inside a computed() or effect(), Angular internally records that relationship. This means Angular knows exactly which computations or template bindings depend on which Signals. When a Signal changes, only the affected parts of the UI are updated — not the entire component. This fine-grained tracking leads to more efficient rendering and better performance compared to traditional change detection.

2. Equality Functions

By default, a Signal triggers updates whenever its value changes by reference. However, you can provide a custom equality function to control when updates should occur. This is useful when working with objects or arrays where structural comparison is preferred over reference comparison. A proper equality function can prevent unnecessary re-renders and improve performance in complex state scenarios

3. Integration with RxJS
Signals can interoperate with Observables using helper functions like toSignal() and toObservable()
Convert Observable -> Signal

Angular provides a convenient way to convert an Observable into a Signal, allowing you to integrate asynchronous streams with the new fine-grained reactivity model. Angular offers the toSignal() utility (from @angular/core/rxjs-interop) to convert an Observable into a Signal.

Example:
import { toSignal } from '@angular/core/rxjs-interop';
data$ = this.http.get<User[]>('/api/users');
dataSignal = toSignal(data$, { initialValue: [] });

When converting an Observable to a Signal, you must provide an initial value. Signals always need a current state, but Observables emit asynchronously. Supplying an initial value ensures the signal has a defined state right from the start, even before the first emission.

Convert Signal -> Observable

Angular provides the toObservable() utility (from @angular/core/rxjs-interop) to convert a Signal into an Observable. It creates an Observable that emits a new value whenever the Signal changes.

Example:
import { toObservable } from '@angular/core/rxjs-interop';
count$ = toObservable(this.countSignal);


This feature is especially handy when you need to apply RxJS operators like map, filter, or switchMap. It enables smooth integration of Signals into existing RxJS-based workflows, services, and third-party libraries.

Benefits of Signals

  • Performance: Minimizes unnecessary re-rendering by updating only the parts of the UI that depend on changed values.
  • Clarity: Clearly defines reactive dependencies, making data flow easier to understand and maintain.
  • Ease of Use: Eliminates the need for manual subscriptions and complex reactive patterns.
  • Future-Ready: Brings Angular in line with modern, fine-grained reactivity approaches.

Conclusion
An important development in Angular's reactivity architecture is Signals. Compared to previous patterns, they provide a more streamlined, effective, and predictable method of handling state. Signals are now the go-to option for handling local state and fine-grained, accurate user interface updates, even though Observables are still essential for overseeing asynchronous processes.



AngularJS Hosting Europe - HostForLIFE :: Combine Angular and New Relic for Error Tracking, API Tracing, and SPA Monitoring

clock February 11, 2026 07:56 by author Peter

New Relic Browser Monitoring provides comprehensive observability for Angular Single Page Applications (SPAs). This article demonstrates how to integrate New Relic into an Angular application using the @newrelic/browser-agent NPM package, providing a more maintainable approach than traditional script-based implementations.

Why Use NPM Package Instead of Script Tag?
Advantages:

  • TypeScript support with full type safety
  • Environment-based configuration management
  • Better integration with Angular's build system
  • Custom service layer for easier usage
  • Framework-specific features (SPA route tracking, HTTP interception)
  • Version control through package. json

Installation
Install the New Relic Browser Agent Package

npm install @newrelic/browse

Configuration
Add New Relic Configuration to Environment Files
export const environment = {
  newRelic: {
    enabled: true,
    accountID: 'YOUR_ACCOUNT_ID',
    trustKey: 'YOUR_TRUST_KEY',
    agentID: 'YOUR_AGENT_ID',
    licenseKey: 'YOUR_LICENSE_KEY',
    applicationID: 'YOUR_APPLICATION_ID'
  }
};


Create separate configurations for different environments (development, staging, production) with appropriate credentials.
Implementation

Step 1: Initialize New Relic in main.ts

The New Relic agent should be initialized after Angular bootstraps.

import { BrowserAgent } from '@newrelic/browser-agent/loaders/browser-agent';
import { enableProdMode } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { environment } from './environments/environment';
import { provideHttpClient, withInterceptorsFromDi, HTTP_INTERCEPTORS } from '@angular/common/http';
import {NewRelicHttpInterceptor} from './app/global/services/newrelic-handler/newrelic-http.interceptor';

// ... other imports and providers

if (environment.production) {
  enableProdMode();
}

// Bootstrap Angular first
bootstrapApplication(AppComponent, {
  providers : [
    // ... your other providers
    provideHttpClient(withInterceptorsFromDi()),

    // NEW RELIC: Register HTTP Interceptor
    {
      provide : HTTP_INTERCEPTORS,
      useClass : NewRelicHttpInterceptor,
      multi : true
    }
  ]
})
    .then(() => {
      if (environment.newRelic?.enabled) {
        setTimeout(() => {
          try {
            // Capture native console functions before agent patches
            const nativeWarn = console.warn.bind(console);
            const nativeError = console.error.bind(console);

            // Initialize New Relic Browser Agent
            new BrowserAgent( {
              init : {
                distributed_tracing : { enabled : true },
                privacy : { cookies_enabled : true },
                ajax : { deny_list : [], enabled : true },
                session_trace : { enabled : true },
                session_replay : {
                  enabled : true,
                  sampling_rate : 10,
                  error_sampling_rate : 100
                },
                jserrors : {
                  enabled : true,
                  harvestConsoleErrors : false  // Don't capture console.error
                } as any,
                logging : {
                  enabled : true,
                  harvestConsoleErrors : false,
                  harvestConsoleWarns : false,
                  harvestConsoleInfo : false
                } as any,
                metrics : { enabled : true },
                page_action : { enabled : true }
              },
              info : {
                beacon : 'bam.nr-data.net',
                errorBeacon : 'bam.nr-data.net',
                licenseKey : environment.newRelic.licenseKey,
                applicationID : environment.newRelic.applicationID,
                sa : 1
              },
              loader_config : {
                accountID : environment.newRelic.accountID,
                trustKey : environment.newRelic.trustKey,
                agentID : environment.newRelic.agentID,
                licenseKey : environment.newRelic.licenseKey,
                applicationID : environment.newRelic.applicationID
              }
            });

            // Restore native console functions to prevent console logs from
            // being sent to New Relic
            console.warn = nativeWarn;
            console.error = nativeError;

            // Optional: Customize New Relic logging behavior
            const nr : any = (window as any).newrelic;
            if (nr?.log) {
              const originalLog = nr.log.bind(nr);
              nr.log = function(message: string, attributes ?: any) {
                const enhancedAttributes = { ... attributes };
                return originalLog(message, enhancedAttributes);
              };
            }
          } catch (error) {
            console.error('New Relic initialization failed:', error);
          }
        }, 100);
      }
    })
    .catch(err => console.log(err));

  • Initialize after Angular bootstrap to ensure proper timing
  • Capture and restore console functions to prevent console logs from being sent to New Relic
  • Wrap initialization in try-catch for error handling

Step 2: Create New Relic Service Wrapper
Create a service to wrap New Relic functionality: src/app/global/services/newrelic-handler/newrelic.service.ts
import { Injectable } from '@angular/core';
@Injectable({providedIn: 'root'})
export class NewRelicService {
  private isInitialized = false;

  constructor() {
    // Check if New Relic is already initialized (from main.ts)
    if ((window as any).newrelic) {
      this.isInitialized = true;
    }
  }

  /**
   * Report custom error to New Relic
   * @param error - Error object
   * @param customAttributes - Additional attributes to track
   */
  noticeError(error: Error, customAttributes?: Record<string, any>): void {
    if (!this.isReady()) return;

    try {
      const attributes = {
        timestamp: new Date().toISOString(),
        errorName: error.name,
        errorMessage: error.message,
        errorStack: (error as any).originalStack || error.stack,
        userAgent: navigator.userAgent,
        url: window.location.href,
        ...customAttributes
      };
      const nr = (window as any).newrelic;

      if (nr.log) {
        Object.entries(attributes).forEach(([key, value]) => {
          if (value !== undefined && value !== null && typeof value !== 'object') {
            try {
              nr.setCustomAttribute(key, value);
            } catch {}
          }
        });
        nr.log(error.message || 'Error occurred', {
          level: 'ERROR',
          ...attributes
        });
      }
    } catch (e) {
      console.error('New Relic error reporting failed:', e);
    }
  }

  /**
   * Track custom user action/event
   * @param name - Name of the action
   * @param attributes - Custom attributes for the action
   */
  addPageAction(name: string, attributes?: Record<string, any>): void {
    if (!this.isReady()) return;

    try {
      (window as any).newrelic.addPageAction(name, {
        ...attributes,
        timestamp: new Date().toISOString(),
        url: window.location.href,
        userAgent: navigator.userAgent
      });
    } catch (e) {
      console.error('New Relic page action failed:', e);
    }
  }

  /**
   * Set custom attribute for the current session
   * @param name - Attribute name
   * @param value - Attribute value
   */
  setCustomAttribute(name: string, value: string | number | boolean): void {
    if (!this.isReady()) return;

    try {
      (window as any).newrelic.setCustomAttribute(name, value);
    } catch (e) {
      console.error('New Relic custom attribute failed:', e);
    }
  }

  /**
   * Set user ID for tracking
   * @param userId - Unique user identifier
   */
  setUserId(userId: string): void {
    this.setCustomAttribute('userId', userId);
    this.setCustomAttribute('enduser.id', userId);
  }

  /**
   * Set user information
   * @param userInfo - User information object
   */
  setUserInfo(userInfo: { userId: string; email?: string; name?: string; role?: string }): void {
    if (userInfo.userId) this.setUserId(userInfo.userId);
    if (userInfo.email) this.setCustomAttribute('userEmail', userInfo.email);
    if (userInfo.name) this.setCustomAttribute('userName', userInfo.name);
    if (userInfo.role) this.setCustomAttribute('userRole', userInfo.role);
  }

  /**
   * Track page view manually (useful for SPA)
   * @param pageName - Name of the page/route
   */
  setPageViewName(pageName: string): void {
    if (!this.isReady()) return;

    try {
      (window as any).newrelic.setPageViewName(pageName);
    } catch (e) {
      console.error('New Relic page view name failed:', e);
    }
  }

  /**
   * Add release version for tracking
   * @param version - Application version
   */
  setApplicationVersion(version: string): void {
    this.setCustomAttribute('applicationVersion', version);
    this.setCustomAttribute('release', version);
  }

  /**
   * Check if New Relic is initialized and ready with full API
   */
  isReady(): boolean {
    const nr = (window as any).newrelic;
    const isReady =
      !!nr &&
      typeof nr.addPageAction === 'function' &&
      typeof nr.noticeError === 'function';

    if (isReady && !this.isInitialized) {
      this.isInitialized = true;
    }
    return isReady;
  }

  /**
   * Track custom metric
   * @param metricName - Name of the metric
   * @param value - Metric value
   * @param unit - Unit of measurement (default: 'ms')
   */
  trackMetric(metricName: string, value: number, unit: string = 'ms'): void {
    this.addPageAction('CustomMetric', { metricName, value, unit });
  }
}


    Provides a clean TypeScript interface to New Relic

    Includes readiness checks before making API calls

    Handles errors gracefully

    Adds useful metadata automatically

Step 3: Create Router Tracking Service

For SPAs, tracking route changes is crucial. So, create: src/app/global/services/newrelic-handler/newrelic-router-tracker.service.ts

import { Injectable } from '@angular/core';
import { Router, NavigationStart, NavigationEnd, NavigationError, NavigationCancel, Event } from '@angular/router';
import { NewRelicService } from './newrelic.service';
import { filter } from 'rxjs/operators';

@Injectable({  providedIn: 'root'})
export class NewRelicRouterTrackerService {
  private navigationStartTime: number = 0;
  private currentUrl: string = '';
  private previousUrl: string = '';

  constructor(
    private router: Router,
    private newRelicService: NewRelicService
  ) {}

  /**
   * Start tracking Angular router navigation events
   */
  startTracking(): void {
    if (this.newRelicService.isReady()) {
      this.initializeTracking();
    } else {
      // Use polling with exponential backoff to wait for New Relic
      this.waitForNewRelic();
    }
  }

  /**
   * Wait for New Relic to be ready before initializing tracking
   * Uses polling with exponential backoff
   */
  private waitForNewRelic(attempt: number = 1, maxAttempts: number = 10): void {
    // Start with 100ms, increase with each attempt (100, 200, 400, 800, etc.)
    const delay = Math.min(100 * Math.pow(2, attempt - 1), 3000);

    console.log(`Waiting for New Relic to be ready (attempt ${attempt}/${maxAttempts})...`);

    setTimeout(() => {
      if (this.newRelicService.isReady()) {
        console.log('✓ New Relic is now ready, starting router tracking');
        this.initializeTracking();
      } else if (attempt < maxAttempts) {
        this.waitForNewRelic(attempt + 1, maxAttempts);
      } else {
        console.warn(`New Relic not ready after ${maxAttempts} attempts, router tracking disabled`);
      }
    }, delay);
  }

  /**
   * Initialize the actual tracking once New Relic is ready
   */
  private initializeTracking(): void {
    // Track navigation start
    this.router.events.pipe(
      filter((event: Event): event is NavigationStart => event instanceof NavigationStart)
    ).subscribe((event: NavigationStart) => {
      this.navigationStartTime = performance.now();
      this.previousUrl = this.currentUrl;
      this.currentUrl = event.url;

      this.newRelicService.addPageAction('RouteChangeStart', {
        url: event.url,
        previousUrl: this.previousUrl,
        navigationTrigger: event.navigationTrigger,
        restoredState: event.restoredState ? 'yes' : 'no'
      });
    });

    // Track navigation end (success)
    this.router.events.pipe(
      filter((event: Event): event is NavigationEnd => event instanceof NavigationEnd)
    ).subscribe((event: NavigationEnd) => {
      const duration = performance.now() - this.navigationStartTime;

      this.newRelicService.addPageAction('RouteChangeComplete', {
        url: event.urlAfterRedirects,
        previousUrl: this.previousUrl,
        duration: Math.round(duration),
        status: 'success'
      });

      // Set page view name for better tracking in New Relic
      const pageName = this.extractPageName(event.urlAfterRedirects);
      this.newRelicService.setPageViewName(pageName);

      // Track as successful route change metric
      this.newRelicService.trackMetric('RouteChangeDuration', duration, 'ms');
    });

    // Track navigation errors
    this.router.events.pipe(
      filter((event: Event): event is NavigationError => event instanceof NavigationError)
    ).subscribe((event: NavigationError) => {
      const duration = performance.now() - this.navigationStartTime;

      this.newRelicService.addPageAction('RouteChangeError', {
        url: event.url,
        previousUrl: this.previousUrl,
        duration: Math.round(duration),
        status: 'error',
        errorMessage: event.error?.message || 'Unknown navigation error'
      });

      // Report as error to New Relic
      const error = new Error(`Navigation Error: ${event.error?.message || 'Unknown error'}`);
      this.newRelicService.noticeError(error, {
        errorType: 'NavigationError',
        url: event.url,
        previousUrl: this.previousUrl
      });
    });

    // Track navigation cancel
    this.router.events.pipe(
      filter((event: Event): event is NavigationCancel => event instanceof NavigationCancel)
    ).subscribe((event: NavigationCancel) => {
      const duration = performance.now() - this.navigationStartTime;

      this.newRelicService.addPageAction('RouteChangeCancel', {
        url: event.url,
        previousUrl: this.previousUrl,
        duration: Math.round(duration),
        status: 'cancelled',
        reason: event.reason
      });
    });

    console.log('✓ New Relic Router Tracking started');
  }

  /**
   * Extract a clean page name from URL
   * @param url - Full URL path
   */
  private extractPageName(url: string): string {
    // Remove query parameters and fragments
    let cleanUrl = url.split('?')[0].split('#')[0];

    // Remove leading slash
    if (cleanUrl.startsWith('/')) {
      cleanUrl = cleanUrl.substring(1);
    }

    // If empty, it's the home page
    if (!cleanUrl) {
      return 'Home';
    }

    // Replace slashes with dots and capitalize
    const pageName = cleanUrl
      .split('/')
      .map(part => part.charAt(0).toUpperCase() + part.slice(1))
      .join('.');

    return pageName;
  }

  /**
   * Track specific route manually
   * @param routeName - Name of the route
   * @param metadata - Additional metadata
   */
  trackRouteManually(routeName: string, metadata?: Record<string, any>): void {
    this.newRelicService.addPageAction('ManualRouteTrack', {
      routeName,
      url: window.location.href,
      ...metadata
    });
  }
}

  • Handles timing issues with exponential backoff polling
  • Tracks all router events (start, end, error, cancel)
  • Measures navigation duration
  • Extracts clean page names for better dashboard organization

Step 4: Create HTTP Interceptor
Track API calls automatically: src/app/global/services/newrelic-handler/newrelic-http.interceptor.ts
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpResponse, HttpErrorResponse } from '@angular/common/http';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
import { NewRelicService } from './newrelic.service';

@Injectable()
export class NewRelicHttpInterceptor implements HttpInterceptor {
  constructor(private newRelicService: NewRelicService) {}

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    if (!this.newRelicService.isReady()) {
      return next.handle(req);
    }

    const startTime = performance.now();
    const requestDetails = {
      url: req.url,
      method: req.method,
      urlWithParams: req.urlWithParams
    };

    return next.handle(req).pipe(
      tap(
        event => {
          if (event instanceof HttpResponse) {
            const duration = Math.round(performance.now() - startTime);

            this.newRelicService.addPageAction('APICallSuccess', {
              ...requestDetails,
              statusCode: event.status,
              statusText: event.statusText,
              duration,
              responseType: event.type,
              contentType: event.headers.get('content-type') || 'unknown'
            });

            this.newRelicService.trackMetric(`API_${req.method}_Duration`, duration);
          }
        },
        error => {
          if (error instanceof HttpErrorResponse) {
            const duration = Math.round(performance.now() - startTime);

            this.newRelicService.addPageAction('APICallFailure', {
              ...requestDetails,
              statusCode: error.status,
              statusText: error.statusText,
              errorMessage: error.message,
              errorName: error.name,
              duration
            });

            const errorObj = new Error(`API Error`);

            this.newRelicService.noticeError(errorObj, {
              errorType: 'HttpError',
              httpMethod: req.method,
              apiUrl: req.url,
              apiEndpoint: new URL(req.url).pathname,
              statusCode: error.status,
              statusText: error.statusText,
              errorMessage: error.message,
              serverErrorMessage: error.error?.error || error.error?.message || error.error,
              duration,
              pageUrl: window.location.href,
              pagePath: window.location.pathname,
              timestamp: new Date().toISOString()
            });
          }
        }
      )
    );
  }
}

  • Automatically tracks all HTTP requests
  • Measures API call duration
  • Captures success and failure scenarios
  • Provides detailed error information

Step 5: Initialize Router Tracking in AppComponent
Start router tracking in your root component: src/app/app.ts
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { NewRelicRouterTrackerService } from './global/services/newrelic-handler/newrelic-router-tracker.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  constructor(
    private router: Router,
    private newRelicRouterTracker: NewRelicRouterTrackerService
  ) {}

  ngOnInit() {
    // ... other initialization code

    // Start New Relic router tracking
    this.startNewRelicRouterTracking();
  }

  /**
   * Start New Relic Router Tracking
   */
  private startNewRelicRouterTracking(): void {
    try {
      // Start router tracking for SPA navigation
      this.newRelicRouterTracker.startTracking();
    } catch (error) {
      // Silently fail if New Relic is not available
      console.debug('New Relic router tracking not started:', error);
    }
  }
}


Advanced Features
User Identification
Track user sessions with user information:
import { NewRelicService } from './global/services/newrelic-handler/newrelic.service';

constructor(private newRelicService: NewRelicService) {}

onUserLogin(user: any) {
  this.newRelicService.setUserInfo({
    userId: user.id,
    email: user.email,
    name: user.name,
    role: user.role
  });
}


Custom Events
Track custom business events:
// Track a button click
this.newRelicService.addPageAction('ButtonClick', {
  buttonName: 'SubmitForm',
  formType: 'Contact',
  timestamp: new Date().toISOString()
});

// Track a purchase
this.newRelicService.addPageAction('Purchase', {
  productId: '123',
  amount: 99.99,
  currency: 'USD'
});


Application Version Tracking
Track application versions for release management:
this.newRelicService.setApplicationVersion('1.2.3');

Performance Considerations
New Relic data is batched and sent asynchronously

The agent has minimal performance impact

Use custom attributes sparingly to avoid payload size issues

Conclusion
This implementation provides a robust, production-ready New Relic integration for Angular applications. Which include:

  • Type-Safe Integration: Full TypeScript support
  • SPA Support: Automatic route tracking
  • API Monitoring: Automatic HTTP request tracking
  • Error Tracking: Comprehensive error reporting
  • Custom Events: Flexible event tracking
  • User Tracking: Session and user identification
  • Maintainable: Clean service-based architecture


About HostForLIFE

HostForLIFE is European Windows Hosting Provider which focuses on Windows Platform only. We deliver on-demand hosting solutions including Shared hosting, Reseller Hosting, Cloud Hosting, Dedicated Servers, and IT as a Service for companies of all sizes.

We have offered the latest Windows 2019 Hosting, ASP.NET 5 Hosting, ASP.NET MVC 6 Hosting and SQL 2019 Hosting.


Tag cloud

Sign in