Skip to content

Commit 37c9745

Browse files
Arrakis-LuxEmmanuelRoux
authored andcommitted
fix(tracker): add the option to run tracking outside of angular zone
Cherry-picked from 6d26eac fixes #60
1 parent a97da4a commit 37c9745

File tree

4 files changed

+55
-12
lines changed

4 files changed

+55
-12
lines changed

docs/configuration-reference.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ Available options :
4444
| enableJSErrorTracking | `boolean` | `false` | If set to `true`, enable JS errors tracking. | yes |
4545
| acceptDoNotTrack | `boolean` | `false` | Set whether to not track users who opt out of tracking using <i>Do Not Track</i> setting | yes |
4646
| requireConsent | `MatomoConsentMode` | `MatomoConsentMode.NONE` | Configure user consent requirement. | yes |
47+
| runOutsideAngularZone | `boolean` | `false` | If set to `true`, will run matomo calls outside of angular's NgZone. This may help if the call causes the app to freeze. | yes |
4748

4849
### NgxMatomoRouterModule
4950

projects/tracker/src/lib/configuration.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export const INTERNAL_MATOMO_CONFIGURATION = new InjectionToken<InternalMatomoCo
2121
trackAppInitialLoad: false,
2222
requireConsent: MatomoConsentMode.NONE,
2323
enableJSErrorTracking: false,
24+
runOutsideAngularZone: false,
2425
...requireNonNull(inject(MATOMO_CONFIGURATION, InjectFlags.Optional), CONFIG_NOT_FOUND),
2526
} as InternalMatomoConfiguration),
2627
}
@@ -109,6 +110,9 @@ export interface BaseMatomoConfiguration {
109110

110111
/** Set to `true` to enable Javascript errors tracking as <i>events</i> (with category <i>JavaScript Errors</i>) */
111112
enableJSErrorTracking?: boolean;
113+
114+
/** Set to `true` to run matomo calls outside of angular NgZone. This may fix angular freezes. */
115+
runOutsideAngularZone?: boolean;
112116
}
113117

114118
export interface BaseAutoMatomoConfiguration<

projects/tracker/src/lib/matomo-tracker.service.spec.ts

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { NgZone } from '@angular/core';
12
import { InternalMatomoConfiguration } from './configuration';
23
import { MatomoHolder } from './holder';
34
import {
@@ -15,8 +16,16 @@ const PLATFORM_BROWSER_ID = 'browser';
1516
const PLATFORM_SERVER_ID = 'server';
1617

1718
describe('MatomoTracker', () => {
18-
function createTracker(disabled = false, platform: Object = PLATFORM_BROWSER_ID): MatomoTracker {
19-
return createMatomoTracker({ disabled } as InternalMatomoConfiguration, platform);
19+
function createMockZone(): jasmine.SpyObj<NgZone> {
20+
return jasmine.createSpyObj<NgZone>(['runOutsideAngular']);
21+
}
22+
23+
function createTracker(
24+
config: Partial<InternalMatomoConfiguration> = { disabled: false },
25+
platform: Object = PLATFORM_BROWSER_ID,
26+
ngZone: NgZone = createMockZone()
27+
): MatomoTracker {
28+
return createMatomoTracker(config as InternalMatomoConfiguration, platform, ngZone);
2029
}
2130

2231
beforeEach(() => {
@@ -632,7 +641,7 @@ describe('MatomoTracker', () => {
632641

633642
it('should ignore calls when disabled', () => {
634643
// Given
635-
const tracker = createTracker(true);
644+
const tracker = createTracker({ disabled: true });
636645
(window as any)._paq = undefined;
637646

638647
// Then
@@ -642,7 +651,7 @@ describe('MatomoTracker', () => {
642651

643652
it('should reject all promises when disabled', done => {
644653
// Given
645-
const tracker = createTracker(true);
654+
const tracker = createTracker({ disabled: true });
646655

647656
// Then
648657
tracker
@@ -656,7 +665,7 @@ describe('MatomoTracker', () => {
656665

657666
it('should ignore calls when platform is not browser', () => {
658667
// Given
659-
const tracker = createTracker(false, PLATFORM_SERVER_ID);
668+
const tracker = createTracker({ disabled: false }, PLATFORM_SERVER_ID);
660669
(window as any)._paq = undefined;
661670

662671
// Then
@@ -666,7 +675,7 @@ describe('MatomoTracker', () => {
666675

667676
it('should reject all promises when platform is not browser', done => {
668677
// Given
669-
const tracker = createTracker(false, PLATFORM_SERVER_ID);
678+
const tracker = createTracker({ disabled: false }, PLATFORM_SERVER_ID);
670679

671680
// Then
672681
tracker
@@ -677,4 +686,23 @@ describe('MatomoTracker', () => {
677686
done();
678687
});
679688
});
689+
690+
it('should run commands outside Angular Zone', () => {
691+
// Given
692+
const zone = createMockZone();
693+
const tracker = createTracker({ runOutsideAngularZone: true }, PLATFORM_BROWSER_ID, zone);
694+
let runOutside = false;
695+
696+
zone.runOutsideAngular.and.callFake(fn => {
697+
runOutside = true;
698+
return fn();
699+
});
700+
701+
// When
702+
tracker.ping();
703+
704+
// Then
705+
expect(runOutside).toBeTrue();
706+
expect(window._paq).toEqual([['ping']]);
707+
});
680708
});

projects/tracker/src/lib/matomo-tracker.service.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { isPlatformBrowser } from '@angular/common';
2-
import { Injectable, PLATFORM_ID } from '@angular/core';
2+
import { Injectable, NgZone, PLATFORM_ID } from '@angular/core';
33
import { INTERNAL_MATOMO_CONFIGURATION, InternalMatomoConfiguration } from './configuration';
44
import { initializeMatomoHolder, MatomoHolder } from './holder';
55
import { Getters, RequireAtLeastOne } from './types';
@@ -103,17 +103,18 @@ export interface MatomoInstance {
103103

104104
export function createMatomoTracker(
105105
config: InternalMatomoConfiguration,
106-
platformId: Object
106+
platformId: Object,
107+
ngZone: NgZone
107108
): MatomoTracker {
108109
return config.disabled || !isPlatformBrowser(platformId)
109110
? new NoopMatomoTracker()
110-
: new StandardMatomoTracker();
111+
: new StandardMatomoTracker(ngZone, config);
111112
}
112113

113114
@Injectable({
114115
providedIn: 'root',
115116
useFactory: createMatomoTracker,
116-
deps: [INTERNAL_MATOMO_CONFIGURATION, PLATFORM_ID],
117+
deps: [INTERNAL_MATOMO_CONFIGURATION, PLATFORM_ID, NgZone],
117118
})
118119
export abstract class MatomoTracker {
119120
/**
@@ -1354,7 +1355,10 @@ export abstract class MatomoTracker {
13541355
}
13551356

13561357
export class StandardMatomoTracker extends MatomoTracker {
1357-
constructor() {
1358+
constructor(
1359+
private readonly ngZone: NgZone,
1360+
private readonly config: InternalMatomoConfiguration
1361+
) {
13581362
super();
13591363
initializeMatomoHolder();
13601364
}
@@ -1370,7 +1374,13 @@ export class StandardMatomoTracker extends MatomoTracker {
13701374
}
13711375

13721376
protected push(args: unknown[]): void {
1373-
window._paq.push(trimTrailingUndefinedElements(args));
1377+
if (this.config.runOutsideAngularZone) {
1378+
this.ngZone.runOutsideAngular(() => {
1379+
window._paq.push(trimTrailingUndefinedElements(args));
1380+
});
1381+
} else {
1382+
window._paq.push(trimTrailingUndefinedElements(args));
1383+
}
13741384
}
13751385
}
13761386

0 commit comments

Comments
 (0)