Meteor、Iron Router 和 Google 分析嵌入 API

Meteor, Iron Router, & Google Analytics Embed API

在尝试通过 Meteor 的 accounts-google 包对 Google API 调用进行身份验证后,我决定尝试使用 Analytics 的 Embed API 来降低一些复杂性。我已经能够通过在每个模板上加载客户端库、进行身份验证等来使各个仪表板正常工作,但这当然效率低下并且充满了重复。

加载 Embed API 库、身份验证和视图选择器一次,同时允许模板在使用 Iron Router 加载时检测它们的最佳方法是什么?还是我只是强迫 Meteor 进入它不适合的应用程序?

我添加了一些示例代码来了解现在的结构:

/client/layout.html

<template name="layout">
    <header>
        <h1><a href="{{pathFor 'basic'}}">DDDashboard</a></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

此模板用于 Template.foo.rendered 以防止在嵌入 API 视图选择器完全实例化之前加载仪表板。

<template name="view">
    <div id="view-selector"></div>
</template>

/client/basic.html

仪表板的一般示例 - 嵌入 API 依赖于预定义的元素来呈现。更复杂的仪表板将有更多 <div> 来包含其他 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

在启动时加载 Google 嵌入 API 库。

(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

嵌入 API 库完全加载后,视图选择器将创建为全局变量,因此任何图表或数据对象都可以访问它。

gapi.analytics.ready(function(){
    viewSelector = new gapi.analytics.ViewSelector({
        container: 'view-selector'
    });

    viewSelector.execute();
});

/client/basic.js

使用Template.foo.rendered等待视图选择器加载;如果不是,则图表 div 保持为空。该仪表板包含一个图表;其他人会包含许多但或多或少相同的模式。

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();
        });
    });
};

事实证明非常简单——只需要添加一些 Meteor 糖就可以让事情正常运行。简单解释:在 viewSelector 发生变化时,将当前视图设置为 Session 变量;然后添加 Tracker.autorun() 块以获取更新并让图表自行更新。此外,积极使用 Template.view.created 回调以确保对象以正确的顺序加载并且不会被多次渲染。

/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();
            }
        });
    });
};

我确信这种模式还有改进的余地——将每个会话变量包装到每个仪表板上的 Tracker.autorun() 块中似乎有点多余,但将所有选择器和数据对象保持在本地范围内可能是a 但更安全,更能抵抗用户篡改。也适用于日期选择。