Mixpanel 2.2 in AMD's structured web application - e.g. require.js

I am trying to use Mixpanel event tracking on a single page site based on Backbone.js and require.js.

Looking at the fragment that Mixpanel provides for cutting and pasting on a regular web page, I can say that they deployed their own asynchronous message loading mechanism, the actual Mixpanel API from an offline resource, do the additional work of setting up people and other attributes and finally output the mixpanel object through the global namespace.

I tried adding shim configuration entries for the fragment or standalone API, but it doesn't work well.

In the course of my research, I found a project on github that does exactly what I want, however it is several years old and is based on the "old" mixpanel API. In the new version of Mixpanel made some non-trivial changes to the fragment and API, which I just I canโ€™t lower it.

I hope someone understands the Mixpanel and / or AMD fragment and requires .js, and I can help me with this.

+7
requirejs amd mixpanel
source share
4 answers

There are two funny things that can solve this problem:

  • The mixpanel library requires the window.mixpanel file to be specified before downloading.
  • Mixpanel lib redefines window.mixpanel as part of the init process.

The source code, the mixpanel fragment does not support get_distinct_id (and any call, which by definition is synchronous), until the library is loaded, but closes other methods (for example, the track) BEFORE loading the mixpanel lib for the queue. Therefore, we have two options:

Option 1. Disable async support and wait for the lib to load - Gist

This method works by creating a pre-init module to configure the dep.maxpanel required for the mixpanel library, and then indicating that it depends on the library itself. Then the mixpanel request will be blocked until the library is fully loaded.

<html> <head> <title>Mixpanel AMD Example - Sync</title> <script type="text/javascript" src="http://requirejs.org/docs/release/2.1.8/minified/require.js"></script> <script type="text/javascript"> requirejs.config({ paths : { 'mixpanel': "//cdn.mxpnl.com/libs/mixpanel-2.2.min" }, shim: { 'mixpanel': { deps: ['mixpanel-preinit'], exports: 'mixpanel' } } }); define("mixpanel-preinit", function(require) { // this is a stripped down version of the mixpanel snippet that removes the loading of the lib via external script tag and the stubs for queuing calls var b=window.mixpanel=window.mixpanel||[];var i,g;b._i=[];b.init=function(a,e,d){function f(b,h){var a=h.split(".");2==a.length&&(b=b[a[0]],h=a[1]);b[h]=function(){b.push([h].concat(Array.prototype.slice.call(arguments,0)))}}"undefined"!==typeof d?c=b[d]=[]:d="mixpanel";b._i.push([a,e,d])};b.__SV=1.2; b.init("YOUR TOKEN"); }); </script> </head> <body> <script type="text/javascript"> require(['mixpanel'], function(mixpanel) { mixpanel.track("my event", {prop1: "val1"}); console.log(mixpanel.get_distinct_id()); }); </script> </body> </html> 

Option 2. Provide a โ€œloadedโ€ callback to update module properties. - gist

If you REALLY want async support, you will need to update your stub methods after loading the mixpanel library. I do not recommend this because (among other reasons) this leads to window.mixpanel! == mixpanel after copy. It also means that you must protect race conditions from synchronous calls such as get_distinct_id (). If lib is not loaded yet, it will be undefined. NOTE. I recommend that if you should have async support, you should just call through window.mixpanel instead of all this craziness.

 <html> <head> <title>Mixpanel AMD Example - Async</title> <script type="text/javascript" src="http://requirejs.org/docs/release/2.1.8/minified/require.js"></script> <script type="text/javascript"> requirejs.config({ paths : { 'mixpanel-lib': "//cdn.mxpnl.com/libs/mixpanel-2.2.min" } }); define("mixpanel", function(require) { var b = window.mixpanel || []; if (!b.__SV) { var i, g; window.mixpanel = b; b._i = []; b.init = function (a, e, d) { function f(b, h) { var a = h.split("."); 2 == a.length && (b = b[a[0]], h = a[1]); b[h] = function () { b.push([h].concat(Array.prototype.slice.call(arguments, 0))) } } var c = b; "undefined" !== typeof d ? c = b[d] = [] : d = "mixpanel"; c.people = c.people || []; c.toString = function (b) { var a = "mixpanel"; "mixpanel" !== d && (a += "." + d); b || (a += " (stub)"); return a }; c.people.toString = function () { return c.toString(1) + ".people (stub)" }; i = "disable track track_pageview track_links track_forms register register_once alias unregister identify name_tag set_config people.set people.set_once people.increment people.append people.track_charge people.clear_charges people.delete_user".split(" "); for (g = 0; g < i.length; g++) f(c, i[g]); b._i.push([a, e, d]) }; b.__SV = 1.2 } // go ahead and start loading the mixpanel-lib require(['mixpanel-lib']); b.init("YOUR TOKEN", {loaded: function() { // now that we know mixpanel is loaded, copy the prop references to our module def for(var prop in window.mixpanel) { b[prop] = window.mixpanel[prop]; } }}); return b; }); </script> </head> <body> <script type="text/javascript"> require(['mixpanel'], function(mixpanel) { mixpanel.track("my event", {prop1: "val1"}); console.log(mixpanel.get_distinct_id()); // probably undefined }); </script> </body> </html> 
+13
source share

The following solution works for mixpanel api 2.2

add mixpanel with the following adjustment -

 path : { 'mixpanel' : '//cdn.mxpnl.com/libs/mixpanel-2.2.min' } shim : { 'mixpanel' : { exports : 'mixpanel' }, } 

and use the following requirejs module instead of the fragment given by the mixpanel -

 define('mixpanel-snippet', [], function(){ var b = window.mixpanel || []; if (!b.__SV) { var i, g; window.mixpanel = b; b._i = []; b.init = function (a, e, d) { function f(b, h) { var a = h.split("."); 2 == a.length && (b = b[a[0]], h = a[1]); b[h] = function () { b.push([h].concat(Array.prototype.slice.call(arguments, 0))) } } var c = b; "undefined" !== typeof d ? c = b[d] = [] : d = "mixpanel"; c.people = c.people || []; c.toString = function (b) { var a = "mixpanel"; "mixpanel" !== d && (a += "." + d); b || (a += " (stub)"); return a }; c.people.toString = function () { return c.toString(1) + ".people (stub)" }; i = "disable track track_pageview track_links track_forms register register_once alias unregister identify name_tag set_config people.set people.set_once people.increment people.append people.track_charge people.clear_charges people.delete_user".split(" "); for (g = 0; g < i.length; g++) f(c, i[g]); b._i.push([a, e, d]) }; b.__SV = 1.2 } b.init("YOUR TOKEN"); require(['mixpanel'], function(mixpanel){}); return b; }); 

I just took a snippet from mixpanel, removed the asynchronous loading of the mixpanel, and wrapped it in the definition of the requirejs module.

Change โ€œYOUR TOKENโ€ at the bottom of the module.

Call Demand Usage Example -

 require([ 'mixpanel-snippet', ], function (mixpanel) { mixpanel.track("Landing Page with AMD SHIM"); }); 

EDIT: the second is the correct answer after a little modification. the way the mixpanel script works, it is necessary that the init call in the fragment be made before the mixer panel actually loads. the trick is to require mixpanel after calling init.i edited the second answer and deleted the first, and here's the gist

EDIT: Reply to the comment by @johanandren The requirement follows the AMD principle, and the order in which the scripts are loaded is not fixed. If you need to download mixpanel before using mixpanel-snippet, you can use the following hack.

 //at the end of mixpanel-snippet code mentioned above force the script to block until mixpanel is loaded b.init("YOUR TOKEN"); var wait = true; require(['mixpanel'], function(mixpanel){wait = false;}); while(wait){} return b; 

** it violates Async boot functions for AMD, makes the script block plus even in the batila mixpan flag, the download is asynchronous, and availability for initial api calls is not guaranteed

+5
source share

It worked for me. Put your mixpanel snippet in the js / lib directory named mixpanel-snippet.js.

In your app.js application, add the following padding to require.config:

 'mixpanel-snippet': { exports: 'mixpanel' } 

In your require function, add 'mixpanel-snippet' to the required array and initialize the mixpanel:

 require(['jquery', 'backbone', 'app/router', 'mixpanel'], function ($, Backbone, Router) { var router = new Router(); Backbone.history.start(); mixpanel.init(key); mixpanel.track("Landed on Start up Page"); }); 

I can provide a complete example of app.js if it helps, but it should start. Let me know if this works.

0
source share

According to mixpanel 2.7.x, they now support several versions of AMD / UMD that are compatible with requirejs, you can capture them on your github page: https://github.com/mixpanel/mixpanel-js

 requirejs(['./mixpanel.amd'], function(mixpanel) { mixpanel.init("FAKE_TOKEN", { debug: true, loaded: function() { mixpanel.track('loaded() callback works but is unnecessary'); alert("Mixpanel loaded successfully via RequireJS/AMD"); } }); mixpanel.track('Tracking after mixpanel.init'); }); 
0
source share

All Articles