Meteor, Iron Router, & Google Analytics Embed API - javascript

After trying to authenticate Google API calls through Meteor's accounts-google package, I decided to try Analytics' Embed API to cut down on some complexity. I've been able to get individual dashboards working by loading the client library, authenticating, etc. on each template, but of course that's inefficient and filled with repetition.
What's the best way to load the Embed API library, authentication, and view selector once while allowing templates to detect them as they're loaded with Iron Router? Or am I simply forcing Meteor into an application it's not well-suited for?
I've added some sample code to give an idea of how things are structured now:
/client/layout.html
<template name="layout">
<header>
<h1>DDDashboard</h1>
</header>
<section id="admin">
<div id="auth-button"></div> <!-- Outlet for Embed API's auth -->
{{> view}} <!-- Outlet for the View template -->
</section>
<section id="main">
{{> yield}} <!-- Outlet for each dashboard -->
</section>
</template>
/client/view.html
This template is used in a Template.foo.rendered to prevent the dashboard from loading until the Embed API view selector is fully instantiated.
<template name="view">
<div id="view-selector"></div>
</template>
/client/basic.html
Generic example of a dashboard—the Embed API relies on predefined elements to render into. More complex dashboards would have more <div> to contain other charts/info.
<template name="basic">
<div id="data-container"></div> <!-- Outlet for the Embed API chart -->
</template>
/lib/router.js
Router.configure({
layoutTemplate: 'layout'
});
Router.route('/', {
name: 'basic'
});
/client/lib/gapi.js
Loads the Google Embed API library at launch.
(function(w,d,s,g,js,fs){
g=w.gapi||(w.gapi={});g.analytics={q:[],ready:function(f){this.q.push(f);}};
js=d.createElement(s);fs=d.getElementsByTagName(s)[0];
js.src='https://apis.google.com/js/platform.js';
fs.parentNode.insertBefore(js,fs);js.onload=function(){g.load('analytics');};
}(window,document,'script'));
/client/auth.js
gapi.analytics.ready(function(){
gapi.analytics.auth.authorize({
container: 'auth-button',
clientid: 'INSERT-CLIENTID'
});
});
/client/view.js
Once the Embed API library is fully loaded, the view selector is created as a global variable so any chart or data objects can access it.
gapi.analytics.ready(function(){
viewSelector = new gapi.analytics.ViewSelector({
container: 'view-selector'
});
viewSelector.execute();
});
/client/basic.js
Uses Template.foo.rendered to wait for the view selector to load; if it doesn't, the chart div remains empty. This dashboard contains one chart; others would contain many but more or less the same pattern.
Template.view.rendered = function(){
gapi.analytics.ready(function(){
var dataChart = new gapi.analytics.googleCharts.DataChart({
query: {
metrics: 'ga:sessions',
dimensions: 'ga:date',
'start-date': '30daysAgo',
'end-date': 'yesterday'
},
chart: {
container: 'data-container',
type: 'LINE',
options: {
width: '100%'
}
}
});
viewSelector.on('change', function(ids){
dataChart.set({query: {ids: ids}}).execute();
});
});
};

Turns out to be pretty simple—just needed to add some Meteor sugar to get things behaving properly. Simple explanation: on a change from the viewSelector, set the current view as a Session variable; then add Tracker.autorun() blocks to grab updates and have the chart update itself. Also, be aggressive using Template.view.created callbacks to ensure objects load in the right order and aren't being rendered multiple times.
/client/view.js
Template.view.created = function(){
gapi.analytics.ready(function(){
var viewSelector = new gapi.analytics.ViewSelector({
container: 'view-selector'
});
viewSelector.execute();
viewSelector.on('change', function(ids){
Session.set('currentView', ids);
});
});
};
/client/basic.js
Template.users.created = function(){
gapi.analytics.ready(function(){
dataChart = new gapi.analytics.googleCharts.DataChart({
query: {
metrics: 'ga:users',
dimensions: 'ga:date',
'start-date': '30daysAgo',
'end-date': 'yesterday'
},
chart: {
container: 'data-container',
type: 'LINE',
options: {
width: '100%'
}
}
});
Tracker.autorun(function(){
if(Session.get('currentView')){
dataChart.set({query: {ids: Session.get('currentView')}}).execute();
}
});
});
};
I'm sure there's room to improve with this pattern—wrapping each session variable into a Tracker.autorun() block on every dashboard seems a little superfluous, but keeping all the selectors and data objects locally-scoped is probably a but more secure and resistant to user tampering. Also works with date selection.

Related

Adding multiple Google Analytics charts on the same page

I have a dashboard in which I need to incorporate Google Analytics charts. Each chart is a different widget and can be removed from or added to the dashboard. The charts display properly when there is only ONE added to the dashboard. When I add two, they don't display anything anymore. Could the fact that I call the same Embed API library in each widget cause this?
An example of the code I am using for each widget:
<script>
(function(w,d,s,g,js,fs){
g=w.gapi||(w.gapi={});g.analytics={q:[],ready:function(f){this.q.push(f);}};
js=d.createElement(s);fs=d.getElementsByTagName(s)[0];
js.src='https://apis.google.com/js/platform.js';
fs.parentNode.insertBefore(js,fs);js.onload=function(){g.load('analytics');};
}(window,document,'script'));
</script>
<div id="embed-api-auth-container-1"></div>
<div id="chart-container-1"></div>
<div id="view-selector-container-1"></div>
<script>
gapi.analytics.ready(function() {
gapi.analytics.auth.authorize({
container: 'embed-api-auth-container-1',
clientid: '***'
});
var viewSelector = new gapi.analytics.ViewSelector({
container: 'view-selector-container-1'
});
viewSelector.execute();
var dataChart = new gapi.analytics.googleCharts.DataChart({
query: {
metrics: 'ga:sessions',
dimensions: 'ga:date',
'start-date': '30daysAgo',
'end-date': 'yesterday'
},
chart: {
container: 'chart-container-1',
type: 'LINE',
options: {
width: '100%'
}
}
});
viewSelector.on('change', function(ids) {
dataChart.set({query: {ids: ids}}).execute();
});
});
</script>
Managed to temporarily fix this by calling the Embed API library ONLY in the first widget that appears on the page. Not ideal, but it works for now. If anybody figures out the right solution, I'd like to know.

No data when rendering ga:userGender in my app using Reporting API V4

I am creating simple app that renders Google Analytics data in view.
By using multiple examples I have set up everything. Few queries work without problem, but I can't retrieve info about audience, especially, userGender.
I don't receive any error message in view, but just "No data" message.
My code in view:
<div id="chart-2-container"></div>
<div id="view-selector-2-container"></div>
<script>
var viewSelector2 = new gapi.analytics.ViewSelector({
container: 'view-selector-2-container'
});
viewSelector2.execute();
var dataChart2 = new gapi.analytics.googleCharts.DataChart({
query: {
metrics: 'ga:users',
dimensions: 'ga:userGender',
'start-date': '60daysAgo',
'end-date': 'today',
'max-results': 6,
},
chart: {
container: 'chart-2-container',
type: 'PIE',
options: {
width: '100%',
pieHole: 4/9
}
}
});
viewSelector2.on('change', function(ids) {
dataChart2.set({query: {ids: ids}}).execute();
});
</script>
Note: I just showed code for that query that doesn't work, I have other ones that work great (See attached pic):
I tried the same query in query-explorer and received no data message too.
In Audience - Dimensions & Metrics Explorer under ga:userGender dimension isn't requered any metrics, but in Query Explorer they required metric (See in above example).
What could be possible problem and solution for this?
Thanks.

google analytics embed api authentication with access token

Hi all
I am new for google analytics embed api. And I am trying to use access token in order to get data without user consent. I've got a Key for browser applications in google developer console. But I couldn't figure out how to use it. Here is my code. It doesn't work (I've set the reference to any reference allowed):
Thanks.
<!DOCTYPE html>
<html>
<head>
<title>Embed API Demo</title>
</head>
<body>
<!-- Step 1: Create the containing elements. -->
<section id="auth-button"></section>
<section id="view-selector"></section>
<section id="timeline"></section>
<!-- Step 2: Load the library. -->
<script>
(function(w,d,s,g,js,fjs){
g=w.gapi||(w.gapi={});g.analytics={q:[],ready:function(cb){this.q.push(cb)}};
js=d.createElement(s);fjs=d.getElementsByTagName(s)[0];
js.src='https://apis.google.com/js/platform.js';
fjs.parentNode.insertBefore(js,fjs);js.onload=function(){g.load('analytics')};
}(window,document,'script'));
</script>
<script>
gapi.analytics.ready(function() {
// Step 3: Authorize the user.
gapi.analytics.auth.authorize({
serverAuth: {
access_token: 'XXXXX' // do not share sensible data
}
});
// Step 4: Create the view selector.
var viewSelector = new gapi.analytics.ViewSelector({
container: 'view-selector'
});
// Step 5: Create the timeline chart.
var timeline = new gapi.analytics.googleCharts.DataChart({
reportType: 'ga',
query: {
'dimensions': 'ga:date',
'metrics': 'ga:sessions',
'start-date': '30daysAgo',
'end-date': 'yesterday',
},
chart: {
type: 'LINE',
container: 'timeline'
}
});
// Step 6: Hook up the components to work together.
gapi.analytics.auth.on('success', function(response) {
viewSelector.execute();
});
viewSelector.on('change', function(ids) {
var newIds = {
query: {
ids: ids
}
}
timeline.set(newIds).execute();
});
});
</script>
</body>
</html>

Creating a custom metric for Google Analytics

I have embedded the Google Analytics API to my website (developed using php, yii2 framework). I would like to create a custom metric to include in my chart. The value for my custom metric would be from my database. From the documentation, I tried using the following snippet:
var metric1Value = <?= $modelValue; ?>
ga('set', 'metric1', metric1Value);
This however results in an error stating that 'ga' is undefined. Is it even possible to create a custom metric in Google Analytics? If it is, how can it be achieved?
Here's an overview of my code if it helps. I've followed the tutorial:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<div>Analytics Test</div>
<div id="embed-api-auth-container"></div>
<div id="view-selector-container" style="display:none;"></div>
<div id="date-range-selector-container"></div>
<div id="table-container" style="margin:5%"></div>
<script>
(function(w,d,s,g,js,fs){
g=w.gapi||(w.gapi={});g.analytics={q:[],ready:function(f){this.q.push(f);}};
js=d.createElement(s);fs=d.getElementsByTagName(s)[0];
js.src='https://apis.google.com/js/platform.js';
fs.parentNode.insertBefore(js,fs);js.onload=function(){g.load('analytics');};
}(window,document,'script'));
gapi.analytics.ready(function() {
gapi.analytics.auth.authorize({
container: 'embed-api-auth-container',
clientid: 'myclientid'
});
var dataChart = new gapi.analytics.googleCharts.DataChart({
reportType: 'ga',
query: {
'start-date':'50daysAgo',
'end-date':'yesterday',
'metrics': [['ga:sessions', 'ga:users', 'ga:newUsers', 'ga:pageviews']],
'dimensions': [['ga:date']],
},
chart: {
container: 'table-container',
type: 'TABLE',
options: {
width: '80%'
}
}
});
});
</script>
Any help would be greatly appreciated.
Syntax you're using is for tracking custom metric on your website, and code you're using is for showing report data. If you want to show custom metric then add ga:metric1 in your metric list
e.g.
'metrics': [['ga:sessions', 'ga:users', 'ga:newUsers', 'ga:pageviews']]
will be
'metrics': [['ga:sessions', 'ga:users', 'ga:newUsers', 'ga:pageviews', 'ga:metric1']]
Note: Your metric needs to be configured in Google Analytics Account Panel

Ember.js: showing multiple database-backed resources/models in the same page/route?

I'm writing a small test app using Ember, in the form of a budget manager. I have a Budget object, which contains general properties like the monthly limit and a description. I also have a set of Expense objects, which contain a name, the amount spent, etc. Both are retrieved from a server using Ember Data's REST adapter.
HTML:
<body>
<script type="text/x-handlebars" data-template-name="budget">
<h2>{{name}} (€ {{amount}})</h2>
</script>
<script type="text/x-handlebars" data-template-name="expenses">
<ul id="expense-list">
{{#each model}}
{{render "expense" this}}
{{/each}}
</ul>
</script>
<!-- expense template -->
<script type="text/x-handlebars" id="expense">
<li>
<label>{{description}}</label>
<label class="subtle">{{formatDate time}}</label>
<label class="amount">{{amount}}</label>
</li>
</script>
</body>
</html>
JavaScript:
window.App = Ember.Application.create();
App.ApplicationAdapter = DS.RESTAdapter.extend({
host: 'http://localhost:5000',
namespace: 'outthepocket/api'
});
// Model
App.Expense = DS.Model.extend({
amount: DS.attr('number'),
description: DS.attr('string'),
time: DS.attr('date')
});
App.Budget = DS.Model.extend({
name: DS.attr('string'),
amount: DS.attr('number')
});
// Routes
App.Router.map( function() {
this.resource('budget');
this.resource('expenses');
});
App.ExpensesRoute = Ember.Route.extend({
model: function()
{
return this.store.find('expense');
}
});
App.BudgetRoute = Ember.Route.extend({
model: function()
{
return this.store.find('budget', 1);
}
});
Following the architecture I see in all the Ember tutorials, there is an ExpensesRoute with the list of expenses as its model, and a BudgetRoute with the selected budget as its model. This works great as long as I go through the proper URL to see each resource:
myapp.html#budget renders the budget template with data from the server.
myapp.html#expenses renders the expenses template with data from the server.
The problem I'm having now is that I want to display both templates, with their data, on one page (the index page). I've tried two solutions for this:
Solution 1: Have separate routes and templates and call {{render budget}} and {{render expenses}} in the main application template. This renders both templates, but without any data.
Solution 2: Have just an IndexRoute and return both budget and expenses from its model property, rendering them into the index template. This more or less works, but seems counter to Ember's otherwise nice separation of different resources, routes and controllers.
Any thoughts? I've been through five or six tutorials and Ember's official guide, but none of those have made clear how to assemble a one-page web app with multiple templates backed by multiple resources without having to link to different pages/routes.
You can use Ember.RSVP.hash to load more than one model, in a single object:
App.IndexRoute = Ember.Route.extend({
model: function()
{
return Ember.RSVP.hash({
expenses: this.store.find('expense'),
budget: this.store.find('budget', 1)
})
}
});
And in the template you can access each resolved promise by the key:
{{expenses}} will return the result from this.store.find('expense') promise and {{budget}} the result from this.store.find('budget', 1) promise.
So in your index template you will able to do:
<script type="text/x-handlebars" id="index">
{{render "expenses" expenses}}
{{render "budget" budget}}
</script>

Categories

Resources