Using OpenReplay with Angular
Its important to keep in mind that tracked must run outside of Angular’s Zone.js hooks to prevent overlap and unnecessary checks.
Here is a simple example of tracker setup:
import { Injectable, NgZone } from '@angular/core';
import Tracker from '@openreplay/tracker';
import trackerAssist from '@openreplay/tracker-assist';
@Injectable({
providedIn: 'root',
})
export class OpenReplayService {
public tracker?: Tracker | null;
constructor(private zone: NgZone) {
this.zone.runOutsideAngular(() => {
this.tracker = new Tracker({
projectKey: 'abc123',
ingestPoint: 'https://someurl/',
});
this.tracker.use(
trackerAssist({
confirmText: `You have an incoming call from <Company> Support. Do you want to answer?`,
})
);
});
}
public async start() {
this.zone.runOutsideAngular(() => {
if (this.tracker) {
return this.tracker.start();
} else {
return {
sessionID: null,
sessionToken: null,
userUUID: null,
};
}
});
}
public setUserData(user: { id: string }): void {
this.zone.runOutsideAngular(() => {
if (this.tracker && user.id) {
this.tracker.setUserID(String(user.id));
}
});
}
}
Tracking the HTTP requests
Section titled Tracking the HTTP requestsIf you’re trying to track the requests sent with your Angular application, OpenReplay does not provide a plugin as it does for Fetch or Axios.
That said, you can still configure it to track your requests with the information you want using an HTTPInterceptor.
In this tutorial I’m going to show you how to create an HTTPInterceptor capable of recording both, the requests and responses sent by your HTTPClient.
Creating the interceptor
Section titled Creating the interceptorThe interceptor is a particular type of object you can inject into your App’s code to capture every single request sent with Angular’s default HTTP client and capture the response.
Through this logic, we’ll take advantage of OpenReplay’s custom events, which allow you to send any event you want to be captured inside your session, so we’ll mimic what the Fetch or Axios plugin would do for other setups.
The code for the interceptor is as follows:
import { Injectable } from '@angular/core';
import {
HttpInterceptor,
HttpRequest,
HttpHandler,
HttpEvent,
HttpResponse,
} from '@angular/common/http';
import { Observable } from 'rxjs';
import { filter, map } from 'rxjs/operators';
import { ReplaySessionService } from '../replay-session.service';
@Injectable({providedIn: 'root'})
export class HttpConfigInterceptor implements HttpInterceptor {
constructor(
private replaySessionService: ReplaySessionService,
) { }
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
//This function will be called with the response a few lines below
const handleResponse = (request: HttpRequest<any>, response: HttpResponse<any>, event: string) => {
//we forward our data to the service, which will create the custom event and send it
this.replaySessionService.sendEventToReplaySession(event, { request, response })
}
return next.handle(request).pipe(
//filter out events that aren't http reponses
filter( (event: any) => event instanceof HttpResponse),
map( (resp: HttpResponse<any>) => { //for each response, call handleResponse
handleResponse(request, resp, `${request.url}`)
return resp
}),
map((event: HttpEvent<any>) => {
return event;
})
);
}
}
We’ll look at the replay service in a second, but just assume it’s there right now. Save this file inside your app
folder.
Then edit the app.module
file to add the following inside the @ngModule directive :
providers: [
{provide: HTTP_INTERCEPTORS, useClass: HttpConfigInterceptor, multi: true}
]
So app.module
file should look something like this:
import { NgModule } from '@angular/core';
/*
imports...
*/
import { HttpConfigInterceptor } from './interceptor/index';
@NgModule({
imports: [
/*...*/
],
providers: [
{provide: HTTP_INTERCEPTORS, useClass: HttpConfigInterceptor, multi: true}
],
declarations: [
/*...*/
],
bootstrap: [ AppComponent ]
})
export class AppModule { }
With this out of the way, your application now knows to inject your interceptor on every HTTP request performed.
Now let’s take a look at the actual session replay service.
Creating the SessionReplayService
Section titled Creating the SessionReplayServiceFirst add your new service with the following command:
$ ng generate service replay-session
This will create a new Angular service at the root of your app called replay-session.service.ts
The content of that file should look like this:
import { Injectable } from '@angular/core';
import {
HttpInterceptor,
HttpRequest,
HttpHandler,
HttpResponse,
} from '@angular/common/http';
import OpenReplay from '@openreplay/tracker'
type ReqRespType = {
request: HttpRequest<any>,
response: HttpResponse<any>
}
@Injectable({
providedIn: 'root'
})
export class ReplaySessionService {
tracker: OpenReplay|null = null
constructor() {
this.tracker = new OpenReplay({
projectKey: "<YOUR PROJECT KEY>",
})
//you can set up any other OR plugins here as well
this.tracker.start()
}
sendEventToReplaySession(event: string, params: ReqRespType): void {
const {request, response} = params
this.tracker?.event(event + "[request]", {
method: request.method,
url: request.url,
params: request.params
})
this.tracker?.event(event + "[response]", {
body: response.body,
status: response.status,
headers: response.headers
})
}
}
The constructor for the class, which because this is a service, we know will only be called once, takes care of instantiating the tracker and getting it started.
Then in our sendEventToReplaySession
method, we use the event
method to send two custom events.
If you go back to the interceptor class, you’ll notice that the “event” (the first parameter we get in this method) is actually the URL, so I’m attaching the words “[response]” and “[request]” to them to identify what gets recorded where.
Then I create the payloads for each event saving only the information I want to save.
With this working, here is what you get to see inside the Events tab in your replay:
And if you click on the details of one of these rows, you get the payload we saved:
In fact, you can even take that code and sanitize any field from the request or the response you don’t want visible inside the replay before calling the event
method.
You can check out this repository for the complete source code of a working Angular-based application with the Tracker.
Have questions?
Section titled Have questions?If you have any issues setting up the Tracker on your Angular project, please contact us on our Slack community and ask our devs directly!