We recommend you use SPA2 Monitoring for all SPA frameworks, especially Angular 1–5 and React.

This page describes how to use SPA1 monitoring for AngularJS 1 applications and manually report events for Ember.js applications. This page also provides information about the AngularJS 1 support for SPA1 monitoring.

SPA Requirements

  • IE 10 and Edge is supported. IE >=10 and Edge cannot be in compatibility mode with <=IE 9 browsers. 
  • For non-AngularJS frameworks, the JavaScript Agent >=4.2 is required.

Manual Injection for Angular 1.x Applications

For non-Angular applications, adrum.js needs to be loaded before any other JavaScript on the page. When you are using AngularJS 1, however, you need to inject adrum.js after angular.js, but before angular.js is bootstrapped.

To learn which Angular versions have monitoring support, see Monitoring Support for AngularJS

Manual Injection for Ember.js Applications

For Ember.js applications, you manually inject the JavaScript Agent much in the same way you would do for web pages loaded from an HTTP request. To monitor virtual pages, you manually start and end Virtual Page events based on events triggered when pages are dynamically created. You can also monitor virtual pages to report errors for pages. 

The following sections will show you how to inject the JavaScript Agent and monitor virtual pages.

Manual Injection of the JavaScript Agent

The file app/index.html is the HTML skeleton for all dynamics pages in Ember.js. Thus, you can inject the JavaScript Agent in this file so it is included in every page. 

<!DOCTYPE html>
<html>
  <head>
    ...
    <script charset='UTF-8'>
        window['adrum-start-time'] = new Date().getTime();
        (function(config){
            config.appKey = '<EUM_APP_KEY>';
            config.adrumExtUrlHttp = 'http://cdn.appdynamics.com';
            config.adrumExtUrlHttps = 'https://cdn.appdynamics.com';
            config.beaconUrlHttp = 'http://col.eum-appdynamics.com';
            config.beaconUrlHttps = 'https://col.eum-appdynamics.com';
            config.xd = {enable : false};
        })(window['adrum-config'] || (window['adrum-config'] = {}));
    </script>
    <script src="//cdn.appdynamics.com/adrum/adrum-latest.js"></script>
  </head>
  <body>
    {{content-for "body"}}
    ...
  </body>
</html>
XML
<!DOCTYPE html>
<html>
  <head>
    ...
    <script charset='UTF-8'>
        window['adrum-start-time'] = new Date().getTime();
        (function(config){
            config.appKey = '<EUM_APP_KEY>';
            config.adrumExtUrlHttp = 'http://<your-cdn.com>';
            config.adrumExtUrlHttps = 'https://<your-cdn.com>';
            config.beaconUrlHttp = 'http://col.eum-appdynamics.com';
            config.beaconUrlHttps = 'https://col.eum-appdynamics.com';
            config.xd = {enable : false};
        })(window['adrum-config'] || (window['adrum-config'] = {}));
    </script>
    <script src="//<your-cdn.com>/adrum/adrum-latest.js"></script>
  </head>
  <body>
    {{content-for "body"}}
    ...
  </body>
</html>
XML
<!DOCTYPE html>
<html>
  <head>
    ...
    <script charset='UTF-8'>
        window['adrum-start-time'] = new Date().getTime();
        (function(config){
            config.appKey = '<EUM_APP_KEY>';
            config.adrumExtUrlHttp = 'http://cdn.appdynamics.com';
            config.adrumExtUrlHttps = 'https://cdn.appdynamics.com';
            config.beaconUrlHttp = 'http://col.eum-appdynamics.com';
            config.beaconUrlHttps = 'https://col.eum-appdynamics.com';
            config.xd = {enable : false};
        })(window['adrum-config'] || (window['adrum-config'] = {}));
    </script>
    <script src="//<your-cdn.com>/adrum/adrum.js"></script>
  </head>
  <body>
    {{content-for "body"}}
    ...
  </body>
</html>
XML

Monitor Virtual Pages

When a user requests a new page, the route handler renders the associated template to form the new content of the virtual page. You can listen for events in the route handler indicating when the handler is started and completed, which in effect mark the lifetime of the virtual page. To monitor the virtual page, you start a Virtual Page Event when the activate event is triggered, close the Virtual Page when the deactivate event is triggered, and then report the completed virtual page.

The code snippet below listens to the activate and deactivate events and reports the created virtual page event.

$stringEscapeUtils.escapeHtml($body)
JS

Use Virtual Pages to Capture Errors

You can also create actions in the routing handlers that can be monitored through virtual pages. For example, you might want to monitor Ajax calls. You can create an action that performs the Ajax call and then use a Virtual Page event to capture the results and errors as shown in the code snippet below. 

 actions: {
    makeXhrCall() {
      config.xhrVpView = new ADRUM.events.VPageView();
      config.xhrVpView.start();
      var xmlHttp = new XMLHttpRequest();
      xmlHttp.onreadystatechange = function () {
        if (xmlHttp.readyState === 4) {
          console.log(xmlHttp.responseText);
          config.xhrVpView.end();
          ADRUM.report(config.xhrVpView);
        }
      };
      xmlHttp.open("GET", "http://localhost:3000", true);
      xmlHttp.send(null);
    }
}
JS

Monitoring Support for AngularJS 1

The JavaScript Agent supports monitoring by default for AngularJS versions 1.x

Routing Engines

AngularJS 1 applications that have multiple Views use a route to move from one virtual page to another. You can use Browser RUM to instrument any virtual page that uses either of two routing engines, ngRoute or ui-router.

Metrics

Because virtual pages are constructed in the browser, normal page view metrics must be adjusted. In essence, what a metric for AngularJS 1 must do is correlate the time between various routing events, using their timestamps. Metrics are calculated as follows:

Full Metric NameShort Metric NameCalculation
End User Response Time
(not used for waterfall UI) 
PLTvirtualPageStart to virtualPageEnd
HTML Download TimeDDTviewChangeStart to viewChangeEnd
HTML Download and DOM Building TimeDRTviewChangeStart to viewDOMLoaded
DOM Building TimeDPTviewChangeEnd to viewDOMLoaded
DOM Ready Time
(used instead of PLT for waterfall UI) 
DOMviewChangeStart to viewDOMLoaded

Because the two routing engines function in slightly different ways, what the AppDynamics event consists of differs slightly, based on the engine.  

AppDynamic Event NamengRoute Equivalentui-router Equivalent
virtualPageStartlocationChangeStart
stateChangeStart
viewChangeStartrouteChangeStart
stateChangeStart
viewChangeEndrouteChangeSuccess
stateChangeSuccess
viewDOMLoadedviewContentLoaded
viewContentLoaded (may happen multiple times -  timestamp overwritten each time)
viewFragmentsLoadedload time of the last HTML fragment
xhrRequestsCompletedresponse time of the last XHR requests
viewResourcesLoadedload time of the last resources
virtualPageEndthe latest one among viewContentLoadedxhrRequestsCompleted, and viewResourcesLoaded

Page Load Process Visualized

Visualized the page load process looks something like this:

SPA Page Load Process Diagram

Compare these to the standard page metrics, which are shown in Browser RUM Metrics.

Exclude Heartbeats or Background Requests from Timings

You may wish to exclude certain events from your virtual page timings. To do this, you can customize the JavaScript Agent when you inject it.  

Add the following snippet before you add the JavaScript Agent file, adrum.js, to the page:

Option for excluding XHRs

<script type="text/javascript">
(function(config) { 
   (function(spa) {
        (function(angular) {
            (function(vp) {
                vp.xhr = {
                    "exclude": {
                        "urls": {
                            pattern: '.*/dealActiveUsers'
                        }
                    }
                };
            })(angular.vp || (angular.vp = {}));
        })(spa.angular || (spa.angular = {}));
    })(config.spa || (config.spa = {}));
})(window["adrum-config"] || (window["adrum-config"] = {}));
</script>
XML

Enable Resource Timing Collection for Virtual Pages

By default, Virtual Pages for AngularJS 1 do not include resource timing metrics. You need to set the Angular virtual page property includeResTimingInEndUserResponseTiming to true.

The JavaScript configuration below shows you how to enable resource timing collection for AngularJS 1 virtual pages. The configuration also sets limits for XHR calls per page, the buffer size for resource timings, and sets the flag for clearing resource timing metrics when a beacon is sent.

window['adrum-config'] = { 
    "xhr": { 
        "maxPerPageView": 10000 
    }, 
    "resTiming": {
        bufSize: 300, 
        "clearResTimingOnBeaconSend": true 
    },
    "spa": {
        "angular": {
            "vp": {
                "metrics": {
                    "includeResTimingInEndUserResponseTiming": true
                }
            }
        }
    }
}
JS

View Correlated Server Times

Since there isn't a regular HTML page timing to which correlated server timings can be linked, to view server times you must drill down from the Dashboard or Snapshot virtual page view to the component XHR requests. The server times can be seen there.