如何构建外部API Dashlet

2019年10月28日,我们在主题上进行了一个网络研讨会 如何使用模块加载程序部署到SugCloud代码。一世n that presentation, we demonstrated a Module Loadable Package that would do 3 things:

  1. 通过定制修改UI
  2. 添加后安装脚本
  3. 添加全局javascript.

I'd想花些时间稍微深入了解糖中的模块可加载包装。

用例

所以让's从一个新例子开始。这个包裹将是 将自定义dashlet添加到帐户的记录视图和引导模块。 Dashlet将根据来自当前模块的字段值从外部API中拉出数据。 That's it.

它可以做更多吗?当然。但是,我的意见是,每个包裹都应该是单一的行动。换句话说,* i *创建的每个包都将执行一件事。如果我还想要添加一个新的脚本库,例如将所有电话号码更改为可点击的链接,例如,我会使用这些文件和指示创建一个单独的包 - 即使我此刻可能会更快地为我丢弃随着包裹我正在努力的。

创建和使用小型奇异的封装,让我保持简单的事情。所以,如果某些事情失败,我有合理的代码和文件,以便浏览一切都朝上。当然,可以使参数创建一个可以安装/卸载一次的大包。事实上,这’我们走近所有的方式 教授M.。它是一个包装,绝对加载我们想要的项目的定制。我认为更少于更好(认为微服务)。有一个计划可以改变工作 教授M. 一点点。那时,我们可能会将事情分成更易于达到的碎片。

规划

好的,回到这个项目。我要做的第一件事就是确定这个Dashlet的范围和功能。我知道它应该在帐户上显示并引导模块。我知道它应该只表现出记录视图。让'S设置为最新的主要版本的糖(现在是9.x)。但是德希特究竟会做什么?

由于我的糖实例正在使用 教授M.,我认为向用户显示与他们正在观看的记录相同区域的其他学校的统计数据列表是有意义的。这样,用户可以展示潜在的学生,他们周围的其他学校正在为学费(或作业位置是什么等)。为此,我需要找到数据源。一世 创建了一个免费帐户API..data.gov. 这样我就可以获得一个API键来调用API.Data.gov/ed/collegescorecard/v1/schools的RET端点。我想做的最后一件事就是确保我投入的东西令人赏心悦目。为此,我会添加一些自定义CSS。

包裹

现在我拥有熨烫的所有高级细节,是时候开始创建了 模块可加载包。开始,让'S创建清单。从最后一段中的详细信息,我的清单变量看起来像这样:

$manifest = array(
        'acceptable_sugar_flavors' => array('PRO','ENT','ULT'),
        'acceptable_sugar_versions' => array(
            'regex_matches' => array('9.*.*'),
        ),
        'author' => 'Michael Shaheen',
        'description' => 'Adds College Stats dashlet that pulls data from api.data.gov',
        'is_uninstallable' => true,
        'name' => 'College Stats dashlet',
        'published_date' => date("Y-m-d H:i:s"),
        'type' => 'module',
        'version' => '1.0',
    );

我喜欢做的下一件事是创建文件结构 密封镜在糖类实例中密切镜像。对于这个dashlet,我会把文件放在 /自定义/客户/基本/意见/大学统计 (我这个dashlet的新目录)。在该目录的内部,我们需要3个文件:

  • 大学统计学.JS.:JavaScript控制器 
  • 学院统计室.PHP.:一个元数据php文件,主要定义了dashlet,并使其可用于我的糖实例 
  • 大学统计学:一个把手模板,用于定义布局并显示数据

代码 -"Hello World"

什么具体进入这些文件?让's从元数据文件开始。内 /自定义/客户/基本/意见/大学统计/college-stats.php. 我们将添加我们的dashlet定义,如下所示:

$viewdefs['base']['view']['college-stats'] = array(
     'dashlets' => array(
          array(
               //Display label for this dashlet
               'label' => 'College Stats',

               //Description label for this Dashlet
               'description' => 'Lists tuition statistics for colleges in the same state as the current record. Will show all states if none is specified',

               'config' => array(),
               'preview' => array(),

               //Filter array decides where this dashlet is allowed to appear
               'filter' => array(
                    //Modules where this dashlet can appear
                    'module' => array(
                         'Contacts',
                         'Accounts',
                         'Leads',
                    ),
                    
                    //Views where this dashlet can appear
                    'view' => array(
                         'record',
                    )
               )
          ),
     ),
);

此分配声明在说我们希望添加一个可以在联系人,帐户或引导模块中显示的dashlet,并且仅适用于这些模块的记录视图。

现在我们可以开始填充JavaScript控制器。到 /自定义/客户/基本/意见/大学统计/college-stats.js.,我们将添加以下内容:

({
     plugins: ['Dashlet'],

     _retrieveData: function() {
          this.学校 = "hello";
     },
     
     initialize: function(options) {
          this.学校 = [];
          // call the parent's (View's) initialize function
          // passing options as an array
          this._super('initialize', [options]);
          this._Retriveata.();
     },
})

在这个非常基本的代码中,我们只是声明我们使用的是dashlet插件,初始化将包含我们数据的变量 学校,致电视图's初始化函数,并调用我们将使用的函数从外部源抓取我们的数据。现在,该功能 _Retriveata. 正在设置价值 学校="hello"。这是我们的dashlet的功能控制器。这一切'左图是编写把手模板以显示正在检索的数据。所以,里面 /自定义/客户/基本/意见/大学统计/college-stats.hbs.,我们将为我们的数据添加HTML和占位符:

<div class="control-group dashlet-options">
    <div class="controls controls-two btn-group-fit">
        <div class="row-fluid">
            <div class="">
                <p>This is the internal header of our dashlet</p>
            </div>
        </div>       
    </div>
</div>
<div class="ext_schools">
{{学校}}
</div>

我投入了一些我从其他小缝复制的html,以便看起来有点一致。这个模板中的重要作品是 {{学校}} 线。这将要查看控制器中的数据并显示可变中包含的任何包含的内容 学校.

这些是将在联系人,帐户,带领模块中访问我们的dashlet的文件'录制视图。一旦安装并添加到仪表板,我们将看到一个带头和单词的dashlet"Hello"。长期不太有用,但这是第一步。不幸的是,这是'足以让我们的dashlet进入我们的糖实例。

我从一个完全基于云的位置接近这个项目。我没有用于测试的当地环境 - 只是一个云沙盒实例。这是 不是 我推荐的练习。根本ugh花了很长时间才能工作,进入帐户记录并添加仪表板。只是解释这个过程是乏味的,我遗漏了所有点击和等待进程完成!所以,如果可以,可以在本地建设中进行开发工作(见 开发人员构建 空间在 dev俱乐部社区)在哪里可以直接在您操作的文件中工作。然后,在快乐时,创建包并使用模块加载器将其安装到云实例中。由于我没有这样做,这个项目比我所希望的时间更长。

尽管如此,我坚持不懈。为了让我让我的dashlet进入我的云实例,我必须更新清单来描述与我的每个文件有关。它应该看起来像这样:

    $installdefs = array(
        'id' => 'college-stats-dashlet',
        'copy' => array(
            0 => array(
                'from' => '<basepath>/Files/custom/clients/base/views/college-stats/college-stats.js',
                'to' => 'custom/clients/base/views/college-stats/college-stats.js',
            ),
            1 => array(
                'from' => '<basepath>/Files/custom/clients/base/views/college-stats/college-stats.hbs',
                'to' => 'custom/clients/base/views/college-stats/college-stats.hbs',
            ),
            2 => array(
                'from' => '<basepath>/Files/custom/clients/base/views/college-stats/college-stats.php',
                'to' => 'custom/clients/base/views/college-stats/college-stats.php',
            ),
        ),
    );

现在就是这样'S left do do是拉链包并将其上载/安装到我的云实例中。请记住,您的存档的zip不能包含任何无关的文件。因此,在MAC上,请务必将其与CLI命令zip zip zip

zip -r --filesync ../college-stats.zip * -x"*.DS_Store" -x "*.git*" -x "__MAC*"

这将拉链当前目录和所有子项,不包括与* .ds_store,* .git或__mac *匹配的任何文件。它将将该存档放在当前一个上方的目录中并调用它"college-stats.zip".

上传和安装包后,我可以看到它现在可以作为引线模块的Dashlet访问:

我可以把它添加到我的仪表板上,我可以看到我的"hello"在dashlet中的陈述。

你好 world dashlet example

代码 -With Data

下一步是使用实际数据。我用了 邮差 要对REST API进行一些测试调用。通过这样做,我能够在我找到适当的查询之前,直到我的目的。而且,现在,我将要将一些结果复制/粘贴到我的控制器中的一些结果,以便我可以使用一些数据来使用。为此,我会改变 _Retriveata. 函数返回我的复制数据。喜欢:

_Retriveata.: function() {
    this.学校 = [
        {
            "latest.cost.tuition.out_of_state": 16836,
            "school.school_url": "www.dli.pa.gov",
            "latest.cost.tuition.in_state": 16836,
            "school.name": "Commonwealth Technical Institute",
            "school.state": "PA",
            "id": 212975,
            "latest.earnings.10_yrs_after_entry.working_not_enrolled.mean_earnings": 14600,
            "school.city": "Johnstown",
            "latest.aid.median_debt.completers.overall": null,
            "latest.repayment.1_yr_repayment.completers": null
        },
        {
            "school.name": "Susquehanna County Career and Technology Center",
            "school.state": "PA",
            "id": 441672,
            "school.school_url": "www.scctc-school.org",
            "school.city": "Springville",
            "latest.aid.median_debt.completers.overall": null,
            "latest.repayment.1_yr_repayment.completers": null,
            "latest.earnings.10_yrs_after_entry.working_not_enrolled.mean_earnings": null,
            "latest.cost.tuition.out_of_state": null,
            "latest.cost.tuition.in_state": null
        },
        {
            "latest.repayment.1_yr_repayment.completers": 30,
            "latest.cost.tuition.out_of_state": 21126,
            "school.school_url": "www.brynathyn.edu",
            "latest.cost.tuition.in_state": 21126,
            "school.name": "Bryn Athyn College of the New Church",
            "school.state": "PA",
            "id": 210492,
            "school.city": "Bryn Athyn",
            "latest.aid.median_debt.completers.overall": 26571.5,
            "latest.earnings.10_yrs_after_entry.working_not_enrolled.mean_earnings": null
        },
        {
            "latest.repayment.1_yr_repayment.completers": 55,
            "latest.cost.tuition.out_of_state": 15662,
            "school.school_url": "www.goPMI.org",
            "latest.cost.tuition.in_state": 15662,
            "school.name": "Precision Manufacturing Institute",
            "school.state": "PA",
            "id": 446455,
            "school.city": "Meadville",
            "latest.aid.median_debt.completers.overall": null,
            "latest.earnings.10_yrs_after_entry.working_not_enrolled.mean_earnings": null
        },
        {
            "latest.repayment.1_yr_repayment.completers": 528,
            "school.school_url": "www.empire.edu",
            "school.name": "Empire Beauty School-North Hills",
            "school.state": "PA",
            "id": 450605,
            "latest.earnings.10_yrs_after_entry.working_not_enrolled.mean_earnings": 19900,
            "school.city": "Pittsburgh",
            "latest.aid.median_debt.completers.overall": 10666.5,
            "latest.cost.tuition.out_of_state": null,
            "latest.cost.tuition.in_state": null
        },
        {
            "latest.repayment.1_yr_repayment.completers": 34,
            "school.school_url": "www.cde.edu",
            "school.name": "CDE Career Institute",
            "school.state": "PA",
            "id": 451495,
            "school.city": "Tannersville",
            "latest.aid.median_debt.completers.overall": 6480.0,
            "latest.earnings.10_yrs_after_entry.working_not_enrolled.mean_earnings": null,
            "latest.cost.tuition.out_of_state": null,
            "latest.cost.tuition.in_state": null
        },
        {
            "latest.repayment.1_yr_repayment.completers": 2593,
            "latest.cost.tuition.out_of_state": 11005,
            "school.school_url": "www.mccann.edu",
            "latest.cost.tuition.in_state": 11005,
            "school.name": "McCann School of Business & Technology",
            "school.state": "PA",
            "id": 438212,
            "latest.earnings.10_yrs_after_entry.working_not_enrolled.mean_earnings": 27100,
            "school.city": "Pottsville",
            "latest.aid.median_debt.completers.overall": 24549.5
        },
        {
            "latest.repayment.1_yr_repayment.completers": 125,
            "school.school_url": "www.lcctc.edu",
            "school.name": "Lebanon County Area Vocational Technical School",
            "school.state": "PA",
            "id": 418542,
            "latest.earnings.10_yrs_after_entry.working_not_enrolled.mean_earnings": 38400,
            "school.city": "Lebanon",
            "latest.aid.median_debt.completers.overall": 15485.0,
            "latest.cost.tuition.out_of_state": null,
            "latest.cost.tuition.in_state": null
        },
        {
            "school.name": "Geisinger Commonwealth School of Medicine",
            "school.state": "PA",
            "id": 456542,
            "school.school_url": "//www.geisinger.edu/education",
            "school.city": "Scranton",
            "latest.aid.median_debt.completers.overall": null,
            "latest.repayment.1_yr_repayment.completers": null,
            "latest.earnings.10_yrs_after_entry.working_not_enrolled.mean_earnings": null,
            "latest.cost.tuition.out_of_state": null,
            "latest.cost.tuition.in_state": null
        },
        {
            "latest.repayment.1_yr_repayment.completers": 4120,
            "school.school_url": "www.strayer.edu/pennsylvania/warrendale",
            "latest.cost.tuition.in_state": 13857,
            "school.name": "Strayer University-Warrendale Campus",
            "school.state": "PA",
            "id": 44378405,
            "latest.earnings.10_yrs_after_entry.working_not_enrolled.mean_earnings": 53500,
            "school.city": "Warrendale",
            "latest.aid.median_debt.completers.overall": 34239.5,
            "latest.cost.tuition.out_of_state": null
        }
    ];
}

既然我知道数据结构,我可以编写把手模板以正确显示此数据。看看上面的样本中的数据密钥。每个字段都使用点表示法。因此,如果在把手模板中,我会尝试通过简单使用将其中一个字段放入HTML中 {{school.school_url}} ,什么都不会显示。没有一个叫做的场 学校[0] .school.school_url。但是,有一个叫做的领域 学校[0] [“学校.School_URL”]。要代表在车把上,我们只需将方括号“中的双卷括号内部添加。像这样: {{[学校.school_url]}}。考虑到这一点,我设置了这样的模板:

<div class="control-group dashlet-options">
    <div class="controls controls-two btn-group-fit">
        <div class="row-fluid">
            <div class="">
                <p>This is the internal header of our dashlet</p>
            </div>
        </div>       
    </div>
</div>
<div class="ext_schools">
{{#each学校}}
<div class="row-fluid ext_school">

{{#if [id]}}
<div class="dta_block ext_school_id"><span class="dta_lbl">ID:</span><span class="dta_val">{{[id]}}</span></div>
{{/if}}

{{#if [school.name]}}
<div class="dta_block ext_school_name"><span class="dta_lbl">Name:</span><span class="dta_val">{{[school.name]}}</span></div>
{{/if}}

{{#if [school.city]}}
<div class="dta_block ext_school_city"><span class="dta_lbl">City:</span><span class="dta_val">{{[school.city]}}</span></div>
{{/if}}

{{#if [school.state]}}
<div class="dta_block ext_school_state"><span class="dta_lbl">State:</span><span class="dta_val">{{[school.state]}}</span></div>
{{/if}}

{{#if [school.school_url]}}}
<div class="dta_block ext_school_url"><span class="dta_lbl">Website:</span><span class="dta_val"><a href="{{[school.school_url]}}" target="_blank">{{[school.school_url]}}</a></span></div>
{{/if}}

{{#if [latest.earnings.10_yrs_after_entry.working_not_enrolled.mean_earnings]}}
<div class="dta_block ext_school_earnings10"><span class="dta_lbl">Mean Earnings 10yr After Entry:</span><span class="dta_val">{{[latest.earnings.10_yrs_after_entry.working_not_enrolled.mean_earnings]}}</span></div>
{{/if}}

{{#if [latest.repayment.1_yr_repayment.completers]}}
<div class="dta_block ext_school_repay1yr"><span class="dta_lbl">Loan Repayment After 1yr:</span><span class="dta_val">{{[latest.repayment.1_yr_repayment.completers]}}</span></div>
{{/if}}

{{#if [latest.cost.tuition.in_state]}}
<div class="dta_block ext_school_tuition_instate"><span class="dta_lbl">In-state Tuition:</span><span class="dta_val">{{[latest.cost.tuition.in_state]}}</span></div>
{{/if}}

{{#if [latest.cost.tuition.out_of_state]}}
<div class="dta_block ext_school_tuition_outstate"><span class="dta_lbl">Out-of-state Tuition:</span><span class="dta_val">{{[latest.cost.tuition.out_of_state]}}</span></div>
{{/if}}

</div>
{{/each}}
</div>

那里's在每个元素上的一些简单的html。特定声格的代码包括一个学校变量的循环 {{#each学校}} 和一堆有条件检查以查看该变量是否存在于显示之前存在 {{#if [school.school_url]}}}.

通过,我们应该检查这些变化如何影响我们的Dashlet。所以,我将卸载当前包并上载/安装新包。然后我们可以查看更改。

代码 -Styling

我想我'喜欢我的下一步造型。如果数据不是,它将更容易查看我的更改前进'所有困惑的东西都在dashlet。在以前的示例中,我们添加了一个 无关 凭借我们网站的样式更改的文件'ui。这是一种非常可行的方法。但是,如果另一个包也增加了一个 无关 文件?该文件(如果在我们之后安装)将替换此包中的文件。那's not ideal. In our 构建块Git Repo,有一个插件调用 CSSLOODER.。它将占用CSS文件并将其添加到网站的加载的CSS中。没有涉及重写。权衡是,我们必须使用直接CSS而不是LIGHTJS。如果您已经缺少书写,我喜欢像这样的转换器 //www.webtoolkitonline.com/less-to-css.html。 CSSLoader插件恰好现在已安装在核心应用程序中。因此,我们需要做的就是通过更新我们的插件阵列来引用它在我们的JavaScript控制器中,以包括CSSLoader并将路径添加到我们的CSS文件:

    plugins: ['Dashlet','CssLoader'],
    css: ["/custom/include/css/college-stats.css."],

现在,CSS文件尚不存在。我会在我的包装中创建该文件 /files/custom/include/css/college-stats.css.. 然后添加到我清单的复制阵列中的条目,这些阵列定义将CSS文件放在Sugar实例中的位置。该值与我放入控制器的路径相同 /custom/include/css/college-stats.css..

最后,以下是我们的CSS文件的内容:

.ext_school { padding: 10px; width: 90% !important; margin: 16px auto; border-top: solid 1px #e8e8e8; border-bottom: solid 1px #cacaca; background: #f9f9f9; font-size: 14px; }
.ext_school .dta_block .dta_lbl { display:inline-block; font-weight: 700; color: #003865; margin-right: 4px; }
.ext_school .dta_block .dta_val { display: inline-block; }
.ext_school a { color: #ff8200; text-decoration: none; }
.ext_school a:hover { color: #009cde; text-decoration: underline; }

当我们查看应用程序的新变化时,我们现在将看到一个风格的dashlet:

相当甜蜜!

代码 - 外部数据请求

让'通过从外部REST API中拉动实时数据来继续进行。由于跨站点引用规则,我们无法直接从我们的JavaScript控制器调用外部API。我们的方法'然后,将在我们的Sugar实例中制作自己的自定义API端点,以充当API.Data.gov端点的代理。

这意味着为我们的端点添加文件 /files/custom/clients/base/api/externalcollegestateapi.php.php.。请记住将复制指令添加到此新文件的清单。对于文件的内容,我们需要注册新的端点并添加函数以返回大学统计数据。

我们将添加到我们的文件的第一件事是端点注册码:

<?php

class 外部的CollegeStatsAPI extends 甘蓝
{
public function 注射器()
    {
        return array(
            //GET & POST
            'ExternalCollegeStats' => array(
                //request type
                'reqType' => array('GET'),

                //set authentication
                'noLoginRequired' => false,

                //endpoint path will equal External/CollegeStats/{variable}
                'path' => array('External', 'CollegeStats', '?'),

                //endpoint variables. to access the last value in the url, we will use "state"
                'pathVars' => array('', '', 'state'),

                //method to call
                'method' => 'CallExternal',
            ),
        );
    }

}

所有代码都做了什么?我们首先为扩展的端点创建一个新的类 甘蓝 班级。然后在这一边 注射器 功能,我们必须定义几个值。我们希望这只是一个get要求,所以我们可以设置 'reqType' => array('GET')。一世'd喜欢能够通过前进来调用终点<base_url>/休息/ v11_6 /外部/ collestats / pa在哪里"PA"是我的状态'd喜欢查看结果。要定义该路径,我们需要设置 'path' => array('External', 'CollegeStats', '?')。通过定义的路径,我们需要一种进入的方式 状态 在URL路径末尾的变量。如果我们设置 'pathVars' => array('', '', 'state'),我们可以通过我们呼叫的变量访问该值 状态。最后,在我们的注册函数中,我们需要指定将调用哪种方法来返回数据。所以我们套 'method' => 'CallExternal',然后将函数添加到调用此文件 Callexternal($ API,$ args).

在那个新的 Callickternal. 功能,我们可以添加代码和逻辑以返回数据。当我第一次建立这个包时,在调用外部API之前,我做了一个中介步骤。我将伪数据从JavaScript控制器移动到新端点。我只是告诉端点返回该数组。这实际上是测试我的API端点已正确注册,我可以从中获取数据。然而,对于这篇文章,我'm跳跃进入实际呼叫。

首先,我添加了外部端点的API键和URL:

    $api_key = "XXXXXXXyour-real-key-goes-hereXXXXXXX";
    $base_url = "https://api.data.gov/ed/collegescorecard/v1/schools";

请记住,在您自己的包中更新$ API_KEY值以使用API​​键注册 API..data.gov.

现在让步's添加一个字段数组要发送呼叫:

$data = array(
'api_key' => $api_key,
'per_page' => 50,
'fields' => 'school.name,school.school_url,school.city,school.state,id,latest.aid.median_debt.completers.overall,latest.repayment.1_yr_repayment.completers,latest.earnings.10_yrs_after_entry.working_not_enrolled.mean_earnings,latest.cost.tuition.out_of_state,latest.cost.tuition.in_state'
);

您可以从这里看,我发送了我的API密钥,每页希望看到多少项,以及我想要检索的完整数据集中的字段。注意此端点,该字段'S值必须是逗号分隔的字符串,没有空格。

I'd喜欢检索数据反映当前的糖记录。所以我'll try to grab a "state"从当前记录中的字段并将其发送到此通话。我们'LL在一分钟内绕过此前端逻辑。目前,我们只想确保我们的自定义端点可以处理该参数。

if ( isset($args['state']) && !empty($args['state']) && $args['state'] != "all" ) {
$data['school.state'] = $args['state'];
}

我增加了一个条件 状态 != "all" 因为如果要求所有状态,则端点只需省略该值。

我觉得我们已经完成了我们的设置。现在我们需要制作实际的外部呼叫。开发人员社区中的一些示例和文档使用 file_get_contents. 打电话。不幸的是,这是 黑名单功能 用于云中的模块可加载软件包。包扫描仪将在找到使用此功能时拒绝安装包。所以,我用卷曲。下面的代码应该熟悉任何使用卷曲之前的人。

$ch = curl_init();
$query = http_build_query($data);
$url = $base_url . '?' . $query;
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
$result = curl_exec($ch);

if ( $result !== false ) {
        $response->success = true;
        $response->body = $result;
} else {
        $response->success = false;
        $response->body = curl_error($ch);
        $response->error = curl_errno($ch);
}
curl_close($ch);
return $response;

那'■我们需要为我们的自定义端点提供的代码,该代码将代理我们对外部API的调用。现在我们必须在控制器中编写JavaScript代码来消耗此端点。让's更换虚拟数据 _Retriveata. 方法如下:

var cntrlr = this;

App.API..call('GET', App.API..buildURL('External/CollegeStats/' + "PA"), null, {
     success: function (data) {
          if (data.success == true) {
               var 结果 = JSON.parse(data.body).结果;
               if (结果.length > 0) {
                    cntrlr.学校 = results;

                    // the data needs to be added and rendered
                    _.extend(cntrlr, cntrlr.学校);
                    cntrlr.render();
               }
          }
     },

     error: function (e) {
          throw e;
     }
});

这是一个非常基本的Sugar API通话。我指定了URL路径并添加了"PA"作为现在的国家。在成功回调中,我解析数据并从中获取该节点 - 结果 - 并将其分配给我们的 学校 多变的。记住范围。我不得不设置一个调用的变量 cntrlr =这一点 这样我仍然可以从API回调中引用当前控制器。请注意,这里的另一件事是因为我们在回调和渲染中(可能)已经发生了,我们需要重新调用控制器's render function.

安装此新版本的Dashlet包,我们应该像以前一样看到类似的结果。我们的数据都来自宾夕法尼亚州的状态,因为我们硬编码了。我们如何使这个dashlet抓取并使用我们正在查看的当前模块记录相关的状态?只有更多的javascript:

var currentState;

var currentModule = this.module;

// ensure we are using the correct STATE field for the current module (e.g. Accounts doesn't have a primary_address_state. field)

switch(currentModule) {

     case "Accounts":
          currentState = this.model.get('Billing_address_state.');
          break;
     default:
          currentState = this.model.get('primary_address_state.');
}

var stateAbbrev = cntrlr._getStateAbbreviation(currentState);

首先,我们用来抓住当前模块 这个。odule.。请记住,糖中的每个模块都有不同的领域。我知道,如果我们在账户记录旁边查看这个dashlet,我们应该使用 Billing_address_state. 领域和我们的其他模块都使用 primary_address_state.。所以,我的交换机陈述反映了这一点。不幸的是,来自模块的数据遇到了完整的状态名称(如"Pennsylvania")外部端点需要状态缩写。因此,我制作了一个快速的辅助函数,占据状态名称并返回正确的缩写:

_getStateAbbreviation: function(状态Name) {
     var states = [{"name": "Alabama","abbreviation": "AL"},{"name": "Alaska","abbreviation": "AK"},{"name": "American Samoa","abbreviation": "AS"},{"name": "Arizona","abbreviation": "AZ"},{"name": "Arkansas","abbreviation": "AR"},{"name": "California","abbreviation": "CA"},{"name": "Colorado","abbreviation": "CO"},{"name": "Connecticut","abbreviation": "CT"},{"name": "Delaware","abbreviation": "DE"},{"name": "District Of Columbia","abbreviation": "DC"},{"name": "Federated States Of Micronesia","abbreviation": "FM"},{"name": "Florida","abbreviation": "FL"},{"name": "Georgia","abbreviation": "GA"},{"name": "Guam","abbreviation": "GU"},{"name": "Hawaii","abbreviation": "HI"},{"name": "Idaho","abbreviation": "ID"},{"name": "Illinois","abbreviation": "IL"},{"name": "Indiana","abbreviation": "IN"},{"name": "Iowa","abbreviation": "IA"},{"name": "Kansas","abbreviation": "KS"},{"name": "Kentucky","abbreviation": "KY"},{"name": "Louisiana","abbreviation": "LA"},{"name": "Maine","abbreviation": "ME"},{"name": "Marshall Islands","abbreviation": "MH"},{"name": "Maryland","abbreviation": "MD"},{"name": "Massachusetts","abbreviation": "MA"},{"name": "Michigan","abbreviation": "MI"},{"name": "Minnesota","abbreviation": "MN"},{"name": "Mississippi","abbreviation": "MS"},{"name": "Missouri","abbreviation": "MO"},{"name": "Montana","abbreviation": "MT"},{"name": "Nebraska","abbreviation": "NE"},{"name": "Nevada","abbreviation": "NV"},{"name": "新的 Hampshire","abbreviation": "NH"},{"name": "新的 Jersey","abbreviation": "NJ"},{"name": "新的 Mexico","abbreviation": "NM"},{"name": "新的 York","abbreviation": "NY"},{"name": "North Carolina","abbreviation": "NC"},{"name": "North Dakota","abbreviation": "ND"},{"name": "Northern Mariana Islands","abbreviation": "MP"},{"name": "Ohio","abbreviation": "OH"},{"name": "Oklahoma","abbreviation": "OK"},{"name": "Oregon","abbreviation": "OR"},{"name": "Palau","abbreviation": "PW"},{"name": "宾夕法尼亚州 ","abbreviation": "PA"},{"name": "Puerto Rico","abbreviation": "PR"},{"name": "Rhode Island","abbreviation": "RI"},{"name": "South Carolina","abbreviation": "SC"},{"name": "South Dakota","abbreviation": "SD"},{"name": "Tennessee","abbreviation": "TN"},{"name": "Texas","abbreviation": "TX"},{"name": "Utah","abbreviation": "UT"},{"name": "Vermont","abbreviation": "VT"},{"name": "Virgin Islands","abbreviation": "VI"},{"name": "Virginia","abbreviation": "VA"},{"name": "Washington","abbreviation": "WA"},{"name": "West Virginia","abbreviation": "WV"},{"name": "Wisconsin","abbreviation": "WI"},{"name": "Wyoming","abbreviation": "WY"}];
     if (!_.isUndefined(状态Name) && stateName.length != 2) {
          var obj = _.find(状态s, function (obj) { return obj.name.toLowerCase() === stateName.toLowerCase(); });
          if (!_.isUndefined(obj) && !_.isUndefined(obj.abbreviation)) {
               return obj.abbreviation;
          }
     }
     return "all";
},

如果函数不能为此匹配 状态Name 参数,它将返回文本"all"。您希望在我们的端点定义中回顾,我增加了一个条件"all"。这是必要的,因为我们的路径需要最后值作为参数。

在最终控制器中,您将看到一些其他辅助函数,即我添加了以格式化数据更具可读性。我还为API端点和语言定义添加了一个帮助文件。

我希望这有助于您在那里添加任何需要从外部源带来相关数据的小缝或模块。如果您看到改进空间,请拿走 附件 并使用您的更改运行。然后,在这里与我们分享。或者,一如既往地,您可以联系我们 developers@sugarcrm.com..

19176_college-stats.zip.
匿名的