2015 年 12 月

第 30 卷,第 13 期

此文章由机器翻译。

数据点 - Aurelia 与 DocumentDB 结合: 结合之旅(第 2 部分)

通过 Julie Lerman

Julie Lerman新的 JavaScript 框架、 Aurelia 和 NoSQL 文档数据库服务,Azure DocumentDB 是最近捕获我感兴趣的两个不同的技术。在 6 月,我探讨了较高层面的 DocumentDB (msdn.com/magazine/mt147238)。然后,在 9 月份,我玩游戏与 Aurelia 中的数据绑定 (msdn.com/magazine/mt422580)。通过这两种技术仍好奇,我认为我希望将这两个组合在一起,作为应用程序和 DocumentDB 的前端 Aurelia 用作数据存储区。虽然我的好奇心给我带来了一定的优势 (持久性),我有限的经验与这两种新技术,以及 JavaScript 框架一般情况下,设置我沿多个这样的困境之中尝试地结合使用。没有人最初执行之前此组合的事实后情况已变得更糟 — 至少不公开。我在 11 月的专栏中记录这些失败的尝试 (msdn.com/magazine/mt620011),则这两篇系列文章的第一个。所选的要跳过的一条浅显路径是使用我包装与 DocumentDB 来代替我在我第一 Aurelia 篇文章中从事 Web API 进行交互的现有 Web API。在其中没有挑战性因所做选择此选项,并因此不内容相当有趣。

我最后来到决定使用另一种服务器端解决方案: Node.js。没有使用 DocumentDB Node.js SDK 和一个不同的前端的现有示例。我仔细检查并共享我学到什么与您在我在 11 月的专栏的第二部分中。然后我将有关重新从 9 月列中,我专家的示例创建这次使用作为从 Aurelia 前端到 DocumentDB 我充当中介的 Node.js。各种错误很多,而且它们远远超出与数据相关的问题了。我必须支持从 Aurelia 核心团队的成员,尤其是从 Patrick Walters 加载 (github.com/pwkad),用户不仅可以帮助您确认我已充分利用 Aurelia 功能,但也会间接迫使我进一步 hone 到我的 Git 技能。我将关注这里是涉及访问、 更新和将数据绑定解决方案的各个部分。附带了随附的可下载示例的自述文件说明了如何设置您自己 DocumentDB 并导入此解决方案的数据。它还说明了如何设置运行该示例的要求。

解决方案体系结构

首先,让我们看一看此解决方案的整体体系结构,如中所示 图 1

此解决方案的总体结构
图 1 此解决方案的整体结构

Aurelia 是客户端框架,因此就在这里只是为了处理客户端的要求,如呈现的 HTML,取决于视图和视图模型配对,以及路由、 数据绑定和其他相关任务。(请注意所有客户端代码可以在浏览器,如 Internet Explorer 和 Chrome 中可用的开发人员工具中进行调试。) 在解决方案的路径中,所有客户端代码是在公共/应用程序/src 文件夹中,服务器端代码时的根文件夹中。当运行该 Web 站点时,客户端文件编译通过 Aurelia 以确保浏览器兼容性,且分发到一个名为"dist."文件夹 这些文件是发送到客户端获取的内容。

服务器端代码应熟悉如果您曾在 Microsoft.NET Framework (更接近于我自己背景) 中的 Web 开发。在.NET 环境中,从 web 窗体、 控制器和其他逻辑代码隐藏是传统上编译为 Dll 存在于服务器。当然,大部分内容更改与 ASP.NET 5 (其中我现在感觉更准备好,谢谢到处理此特定项目)。我的 Aurelia 项目还包含服务器端代码,但即便是这样,通过 Node.js 的 JavaScript 代码。此代码不编译为 DLL,但仍保留在其自己单独的文件。更重要的是,它不会下推至客户端并因此不容易公开 (这是,当然,总是非常依赖于安全措施)。如我所述最后一次,他们希望将停留在服务器的代码的推动原因是我需要一种方法来存储我的凭据用于与 DocumentDB 交互。因为我想要试用 Node.js 路径,上述 SDK 进行对话,与 DocumentDB 容易得多。我必须依靠有关如何使用 SDK 的一些基本知识的原始 DocumentDB 节点示例是一个巨大帮助。

我的服务器端代码 (其中我将称它为我的 API) 由四个关键要素组成:

  • 可以轻松地找到充当路由器以确保我的 API 函数 api.js 文件。
  • 我的核心模块、 ninjas.js,其中包含的 API 函数: getNinjas getNinja 和 updateDetails。
  • 控制器类中,执行与 DocumentDb 交互的 DocDbDao (由这些 API 函数调用)。
  • 知道如何确保相关数据库和集合的 DocumentDb 实用程序文件存在。

绑定客户端、 服务器和云

在我早期 Aurelia 示例中,要获取所有 ninjas 的方法进行直接的 HTTP 调用到.NET 和实体框架进行交互与 SQL Server 数据库使用的 ASP.NET Web API:

retrieveNinjas() {
  return this.http.createRequest
    ("/ninjas/?page=" + this.currentPage + "&pageSize=100&query=" +
      this.searchEntry)
      .asGet().send().then(response => {
        this.ninjas = response.content;
    });
  }

已将结果传递给客户端一侧视图和视图模型 (ninjaList.js 和 ninjaList.html),会输出中显示的页面对 图 2

我 Aurelia 的网站中的专家列表
图 2 中我 Aurelia 的网站的专家列表

在新的解决方案中,这种方法,重命名为 getNinjas,现在我的服务器端 Node.js API 调用。我使用更高级的 httpClient.fetch (bit.ly/1M8EmnY) 而不是 httpClient 这次进行调用:

getNinjas(params){
  return this.httpClient.fetch(`/ninjas?q=${params}`)
    .then(response => response.json())
    .then(ninjas => this.ninjas = ninjas);
}

我已配置在其他地方 httpClient 须知的基 URL。

请注意,我提取的方法调用包括术语 ninjas 的 URI。但是,这不指在服务器端 API 中我 ninjas.js。这可能是 /foo — 只是将我的服务器端路由器的解析的随机的引用。我的服务器端路由器 (它使用明确的、 不 Aurelia,因为 Aurelia 只处理客户端) 指定对 api/ninjas 的调用将路由到 ninjas 模块 getNinjas 函数。下面是从 api.js 定义此代码:

router.get('/api/ninjas', function (request, response) {
  ninjas.getNinjas(req, res);
});

现在我 getNinjas 函数 (图 3) 使用字符串插值 (mzl.la/1fhuSIg) 以生成一个字符串来表示用于查询 DocumentDB、 SQL 然后询问用户要执行查询并返回结果的控制器。如果名称属性的筛选器已请求在 UI 中,我将追加到查询的 WHERE 子句。主查询投影只我需要将相关属性页上,包括此 id。(有关在 DocumentDB 投影查询的详细信息请参阅 documentdb.com/sql/demo。) 查询传递到 docDbDao 控制器的查找方法。Find 方法,这是我在本系列的第一部分所述的原始相比并无变化,使用 Node.js SDK 以及 config.js 中存储的凭据来查询 ninjas 的数据库。然后,getNinjas 接受这些结果,并将它们返回到请求它们的客户端。请注意,尽管对 DocumentDB 的调用不作为 JSON 返回结果,但我仍然需要显式使用 response.json 函数将返回的结果传递。这会提醒调用方,则结果是 JSON 格式。

图 3 在 ninjas.js 服务器端模块 getNinjas 函数

getNinjas: function (request, response) {
  var self = this;
  var q = '';
  if (request.query.q != "undefined" && req.query.q > "") {
    q= `WHERE CONTAINS(ninja.Name,'${req.query.q}')`;
  }
  var querySpec = {
    query:
   `SELECT ninja.id, ninja.Name,ninja.ServedInOniwaban,
     ninja.DateOfBirth FROM ninja ${q}`
  };
  self.docDbDao.find(querySpec, function (err, items) {
    if (err) {
      // TODO: err handling
    } else {
      response.json(items);
    }
  })
},

编辑请求的客户端的响应

正如您可以看到在 图 2, ,就可以通过单击铅笔图标编辑专家。下面是我都构建在页标记中的链接:

<a href="#/ninjas/${ninja.id}" class=
  "btn btn-default btn-sm">
  <span class="glyphicon glyphicon-pencil" />
</a>

通过单击我的第一行,其 ID 恰好是"1",编辑图标中,我将获得此 URL:

http://localhost:9000/app/#/ninjas/1

在 app.js 的客户端上使用 Aurelia 路由功能,我已指定在请求此 URL 模式时,它应然后调用编辑模块,id 中给编辑视图模型的激活方法作为参数传递,指示通配符 (* Id):

{ route: 'ninjas/*Id', moduleId: 'edit', title:'Edit Ninja' },

编辑是指与 edit.html 视图成对出现的客户端 edit.js 模块。然后,我编辑的模块的激活函数名为 retrieveNinja,传入的请求 Id 本模块中调用其他函数:

retrieveNinja(id) {
  return this.httpClient.fetch(`/ninjas/${id}`)
    .then(response => response.json())
    .then(ninja => this.ninja = ninja);
}

将编辑请求传递到服务器端 API

同样,我使用 httpClient.fetch 请求 api/ninjas / [id] (在我事例 api/ninjas/1) 从 API。服务器端路由器说,这种模式的请求到达时,它应将路由到 ninjas 模块 getNinja 函数。下面是什么该路由将如下所示:

router.get('/api/ninjas/:id', function(request, response) {
  ninjas.getNinja(request, response);
});

GetNinja 方法然后另一个向发出请求 docDbDao 控制器上,这次是打开 getItem 函数,可从 DocumentDb 获取专家数据。结果是我的 DocumentDB 数据库中存储的 JSON 文档中所示 图 4:

图 4 文档我请求,存储在 DocumentDb Ninjas 收藏

{
  "id": "1",
  "Name": "Kacy Catanzaro",
  "ServedInOniwaban": false,
  "Clan": "American Ninja Warriors",
  "Equipment": [
    {
      "EquipmentName": "Muscles",
      "EquipmentType": "Tool"
    },
    {
      "EquipmentName": "Spunk",
      "EquipmentType": "Tool"
    }
  ],
  "DateOfBirth": "1/14/1990",
  "DateCreated": "2015-08-10T20:35:09.7600000",
  "DateModified": 1444152912328
}

将结果传递回客户端

此 JSON 对象返回给 ninjas.getNinja 函数,然后将其返回给调用方,在此情况下 edit.js 模块在客户端。然后,Aurelia 将 edit.js 绑定到 edit.html 模板,并将输出一个页面,它设计用于显示此关系图中。

编辑视图允许用户修改的数据的四个部分: 专家的名称和出生日期 (这两个字符串),clan (下拉列表) 和专家是否 Oniwaban 中提供服务。Clan 下拉列表中使用一种特殊的 Web 组件调用的自定义元素。自定义元素规范的 Aurelia 实现是唯一的。因为我正在使用该功能来绑定数据,并且这是一个数据列,我将向您展示如何执行该操作。

数据绑定与 Aurelia 自定义元素

类似于其他视图和视图模型对 Aurelia 中的,自定义元素标记的视图和视图模型的逻辑组成。图 5 显示的第三个文件涉及 clans.js,它提供 clans 的列表。

图 5 Clans.js

export function getClans(){
  var clans = [], propertyName;
  for(propertyName in clansObject) {
    if (clansObject.hasOwnProperty(propertyName)) {
      clans.push({ code: clansObject[propertyName], name: propertyName });
    }
  }
  return clans;
}
var clansObject = {
  "American Ninja Warriors": "anj",
  "Vermont Clan": "vc",
  "Turtles": "t"
};

此元素 (dropdown.html) 的视图使用的启动"选择"元素,我仍将视为一个下拉列表:

<template>
  <select value.bind="selectedClan">
    <option repeat.for="clan of clans" model.bind="clan.name">${clan.name}</option>
  </select>
</template>

请注意 value.bind 和 repeat.for,它应是如果您阅读我以前的专栏,在数据绑定与 Aurelia 所熟悉。在 select 元素内的选项将绑定到 clans.js 中定义的 clan 模型,然后显示 clan 名称。我 Clans 对象非常简单,因为具有名称和代码中 (这是在此示例中额外),我可以简单地使用 value.bind 存在。但是,我将坚持使用 model.bind,因为它是一种更好的我记住模式。

Dropdown.js 模块连线具有 clans,标记中所示 图 6

图 6 自定义元素中 Dropdown.js 的模型代码

import $ from 'jquery';
import {getClans} from '../clans';
import {bindable} from 'aurelia-framework';
export class Dropdown {
  @bindable selectedClan;
  attached() {
    $(this.dropdown).dropdown();
  }
  constructor() {
    this.clans = getClans();
  }
}

此外,自定义元素就可以让我在我的编辑视图使用很多简单标记:

<dropdown selected-clan.two-way="ninja.Clan"></dropdown>

请注意,我正在使用以下两个 Aurelia 特定功能。首先,我的属性称为 selectedClan 在视图模型,但选择 clan 在标记中。Aurelia 约定,基于 HTML 的必要性属性来为小写,是使所有自定义属性的导出名称小写和断字,因此其结果应为驼峰式大小写字母的开始位置的连字符。其次,而不 value.bind,我显式使用双向绑定,此处这样 clan 将重新绑定到专家所选内容更改时。

更重要的是,我可以非常轻松地重复使用我在其他视图中的自定义元素。在本例中,它只需提供可读性,因此,多个可维护性。但在大型应用程序,重复使用的标记和逻辑是很大的收益。

将编辑推回 DocumentDB

我的标记是读取关系图中的设备的两个参数,并显示它们。为了简洁起见,我将编辑另一天将设备的数据。

现在工作的最后一位是将备份的更改传递给 API 并将它们发送到 DocumentDB。这会触发与保存按钮。

保存按钮的标记使用另一个 Aurelia 范例,click.delegate—which 使用 JavaScript 事件委托,从而使我能够委托给存储操作 edit.js 中定义的函数。

保存工作中, 所示 图 7, ,创建一个新对象,ninjaRoot,使用从 getNinja,这是在其中定义的专家属性相关的属性,然后绑定到标记中,这样就允许用户从浏览器进行更新。

图 7 ninjas.save 函数

save() {
  this.ninjaRoot = {
    Id: this.ninja.id,
    ServedInOniwaban: this.ninja.ServedInOniwaban,
    Clan: this.ninja.Clan,
    Name: this.ninja.Name,
    DateOfBirth: this.ninja.DateOfBirth
  };
  return this.httpClient.fetch('/updateDetails', {
    method: 'post',
    body: json(this.ninjaRoot)
  }).then(response => {this.router.navigate('ninjaList');
  });
}

保存然后使用现在应该很熟悉 httpClient.fetch 请求调用 udpateDetails API URL,然后将传入的 ninjaRoot 对象作为正文。此外,请注意,我指定的这作为 post 方法,不具有 get。API 路由器告知 Node.js 可将路由到 ninjas 模块的 updateDetails 方法。

router.post('/api/updateDetails', function(request,response){
  ninjas.updateDetails(request,response);
});

现在让我们看一下 ninjas.js 中的服务器端 updateDetails 方法:

updateDetails: function (request,response) {
  var self = this;
  var ninja = request.body;
  self.docDbDao.updateItem(ninja, function (err) {
    if (err) {
      throw (err);
    } else {
      response.send(200);
    }
  })
},

我提取存储在请求正文和专家变量,然后将该变量传递到控制器的 updateItem 方法的集 ninjaRoot。作为 图 8 所示,我修改 updateItem 有点以来第一部分我的文章以适应我专家的类型。

图 8 updateItem 方法中 docDbDao 控制器使用说到 DocumentDB Node.js SDK

updateItem: function (item, callback) {
  var self = this;
  self.getItem(item.Id, function (err, doc) {
    if (err) {
      callback(err);
    } else {
      doc.Clan=item.Clan;
      doc.Name=item.Name;
      doc.ServedInOniwaban=item.ServedInOniwaban;
      doc.DateOfBirth=item.DateOfBirth;
      doc.DateModified=Date.now();
      self.client.replaceDocument(doc._self, doc, function (err, replaced) {
        if (err) {
          callback(err);
        } else {
          callback(null, replaced);
        }
      });
    }
  });
},

UpdateItem 文档使用 id 从数据库进行检索、 更新该文档的相关属性,然后使用 SDK DocumentDBClient.replaceDocument 方法将更改推送到我的 Azure DocumentDB 数据库。回调提醒我的操作已完成。通过 updateDetails 方法,它将返回到调用该 API 的客户端模块返回 200 响应代码,然后记下该回调。如果您回顾一下 save 方法的客户端,您会注意到其回调将路由到 ninjaList。因此更新已成功发布,是显示给用户的 ninjas 的原始列表页。向专家刚刚已更改的任何编辑都将在该列表中可见。

我们是否 Ninjas 尚未?

此解决方案将使我的困难和死胡同原来的虚空到。在我的核心目标能够 Aurelia 交谈 DocumentDB 数据库时,我还想要使用这些技术来利用它们带来的好处。这意味着无需理解这么多世界物流的 JavaScript、 管理节点安装,请使用 Visual Studio Code 第一次由于它可以进行调试 Node.js 和甚至更多地了解 DocumentDB。尽管有可能想要在甚至像我这样一个简单的示例中执行操作的许多其他课题,本文应为您提供的操作的运行方式从一端到另一个基本的了解。

请记住的重要一点是,DocumentDB — 像任何 NoSQL 数据库 — 适用于大容量数据。它是不经济有效的小小位的数据,如我在我的示例中使用。不过,若要浏览的功能与数据库连接和交互数据时,大量的数据不是必需的因此五个对象 sufficed。


Julie Lerman 是 Microsoft MVP、.NET 导师和顾问,住在佛蒙特州的山区。您可以在全球的用户组和会议中看到她对数据访问和其他 .NET 主题的演示。她的博客网址 thedatafarm.com /blog 和代码优先和 DbContext 版,均出自 O'Reilly Media 以及是 《 Programming Entity Framework 》 的作者。请关注她的 Twitter: @julielerman 并查看其 Pluralsight 课程,网址 juliel.me /ps-videos

感谢以下技术专家对本文的审阅: Patrick Walters
Patrick Walters 已经很好的 Aurelia 开发人员社区人员喜欢产品 ear 官方 Aurelia gitter 通道中有关的问题的一员。