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)。由這兩種技術仍然興致,我以為我想要放在一起,這兩個使用 Aurelia 做為應用程式和 DocumentDB 的前端,做為資料存放區。雖然我好奇心給我有一個優點 (持續性),我有限的經驗,這兩種新技術與 JavaScript 架構一般情況下,設定我沿著碰到嘗試將它們結合的次數。這更嚴重的人已經完成之前,此組合 — 至少不公開。這些失敗的嘗試已記載於 11 月的專欄 (msdn.com/magazine/mt620011),則這兩篇式連載的第一次。我選擇略過的一個明顯且容易路徑是使用我現有的 Web API 包裝取代 Web API,我曾經在第一篇文章中 Aurelia 與 DocumentDB 互動。我對這項選擇,因為在它沒有任何挑戰 — 因此未更有趣。

我最後是決定来使用不同的伺服器端解決方案: Node.js。沒有現有的範例會使用 DocumentDB Node.js SDK 和不同的前端。我仔細檢查它,並共用什麼我所學到與您在 11 月的專欄的第二個部分。然後我設定相關這次為我做為中間人 Aurelia 前端從 documentdb 中使用 Node.js 重新建立從 9 月專欄中,我忍者的範例。障礙區塊許多,而且它們遠超越資料相關的問題。我的支援從 Aurelia 核心小組的成員,以及特別是從 Patrick Walters (github.com/pwkad),使用者不僅可以協助確定我已利用 Aurelia 功能,但也會間接地強制我進一步 phone 我 Git 的技能。我將著重在這裡會涉及存取、 更新和繫結資料的方案項目。使用隨附的可下載範例隨附的讀我檔案會說明如何設定您自己的 DocumentDB,並匯入此解決方案的資料。它也說明如何設定執行此範例的需求。

解決方案架構

首先,讓我們看一下此解決方案的整體架構中所示 圖 1

這個解決方案的整體結構
圖 1 此解決方案的整體結構

Aurelia 是用戶端架構,因此這裡只是用來處理用戶端的需求,例如呈現 HTML,以檢視和檢視模型配對,以及路由、 資料繫結和其他相關的工作。(請注意,所有用戶端程式碼才能進行偵錯在 Internet Explorer 和 Chrome 之類的瀏覽器所提供的開發人員工具)。 在方案的路徑中,所有用戶端程式碼時,在公用/應用程式/src 資料夾中,位於根資料夾中的伺服器端程式碼。用戶端檔案執行的網站時,會由 Aurelia 以確保瀏覽器相容性編譯,而且分佈在名為 「 dist.」 的資料夾 這些檔案是什麼被傳送到用戶端。

伺服器端程式碼應該很熟悉,如果您曾做過 Microsoft.NET Framework (接近我自己的背景) 中的 Web 開發的。在.NET 環境中,您從 WebForms、 控制站和其他邏輯的程式碼後置傳統上會編譯至即時伺服器的 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。
  • 控制器類別中,DocDbDao (這些 API 的函式呼叫),用於執行與 DocumentDb 互動。
  • 知道如何確保相關資料庫和集合的 DocumentDb 公用程式檔案存在。

設定用戶端、 伺服器和雲端

在我先前 Aurelia 範例中,取得所有忍方法進行直接與 SQL Server 資料庫搭配使用.NET 和 Entity Framework 互動的 ASP.NET Web API 的 HTTP 呼叫:

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

我已經知道的基底 URL 設定 httpclient,而在其他地方。

請注意我擷取方法呼叫包含詞彙忍的 URI。但這不指我 ninjas.js 在伺服器端 API。可能是--/foo — 只會解決我的伺服器端路由器的隨機的參考。我的伺服器端路由器 (這會使用 Express edition 不 Aurelia,因為 Aurelia 只會處理用戶端) 會指定呼叫 api/忍 getNinjas 函式忍模組的路由。以下是從 api.js 定義此程式碼:

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

現在我 getNinjas 函式 (圖 3) 會使用字串插補 (mzl.la/1fhuSIg) 來建置一個字串,代表 SQL 來查詢 DocumentDB 中,然後要求的控制站來執行查詢並傳回結果。如果篩選器名稱屬性已要求在 UI 中,將附加到查詢的 WHERE 子句。主要查詢只我需要的相關屬性投影在頁面上,包括該識別碼。(有更多有關 DocumentDB 投影查詢在 documentdb.com/sql/demo。) 將查詢傳遞給 find 方法 docDbDao 控制站。Find 方法,也就是不變,與原始我這一系列的第一個部分中所述,使用 Node.js SDK config.js 中儲存的認證以及資料庫忍查詢。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>

依序按一下 [編輯] 圖示,我的第一個資料列,其識別碼剛好是"1",取得此 URL:

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

在 app.js 的用戶端上使用 Aurelia 路由功能,我指定,要求此 URL 模式時,它應該接著呼叫編輯模組並傳入識別碼 activate 方法的編輯檢視模型做為參數,以萬用字元 (* 識別碼):

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

編輯指的是與 edit.html 檢視成對的用戶端 edit.js 模組。啟用函式的編輯模組然後呼叫其他函式,在此模組中名為 retrieveNinja,傳入的要求識別碼:

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

將編輯要求傳遞至伺服器端 API

同樣地,我使用 httpClient.fetch 要求 api/忍 / [id] (在我寫 api/忍/1) 從 API。伺服器端路由器是說,此模式的要求時,它應該路由傳送至忍模組的 getNinja 函式。以下是該路由的外觀如下:

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

GetNinja 方法再發出另一個要求到 docDbDao 控制器,這次 getItem 函式,以取得從 DocumentDb 忍者資料。結果會儲存在我的 DocumentDB 資料庫中的 JSON 文件中所示 圖 4:

圖 4 文件所要求,其儲存在 DocumentDb 忍集合

{
  "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 範本,並將輸出] 頁面上,可以用來顯示此圖表。

[編輯] 檢視可讓使用者修改資料的四個部分: 忍者的名稱以及出生日期 (這兩個字串),氏族 (下拉式) 及忍者是否 Oniwaban 中提供服務。氏族下拉式清單中會使用一種特殊的 Web 元件稱為 「 自訂項目。Aurelia 實作的自訂項目規格是唯一的。因為我要繫結資料,使用該功能,而且這是資料行,可讓我為您示範如何完成。

資料繫結與 Aurelia 自訂項目

如同其他檢視和 Aurelia 中的檢視模式組,自訂項目包含標記的檢視和檢視模型的邏輯。圖 5 顯示的第三個檔案所包含,clans.js,提供一份這條通道。

圖 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 與資料繫結。在選取的項目選項繫結至 clans.js 中定義的氏族模型,並接著顯示氏族名稱。我這條通道物件很簡單,因為只有名稱和程式碼 (這是在此範例中沒有直接關聯),我可以只使用 value.bind 那里。但我接管使用 model.bind,因為它是記住的好模式。

Dropdown.js 模組將這條通道,以標記中所示 圖 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 在檢視模型中,但選取氏族標記中。Aurelia 慣例,依據屬性必須為小寫,HTML 的需求是要讓所有的匯出名稱大小寫的自訂屬性,並無連字號,因此它會預期該連字號是依照 camel 命名法的大小寫慣例的字母開始的位置。第二,而不 value.bind,我明確地使用雙向繫結,這裡以便氏族會重新繫結至忍者選取變更時。

更重要的是,我可以非常輕鬆地重複使用我在其他檢視中的自訂項目。在我的情況下,它只是提供可讀性更高,因此,多個可維護性。但在較大的應用程式中重複使用的標記和邏輯是極大的好處。

編輯推送回到 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 來路由傳送至忍模組的 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 控制器中使用 Node.js SDK 來與 DocumentDB 的交談

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 使用識別碼,從資料庫擷取文件、 更新的文件相關的屬性,然後再使用 SDK DocumentDBClient.replaceDocument 方法將變更發送至 Azure DocumentDB 資料庫。回呼通知我作業的完成。回撥會接著註明 updateDetails 方式回至用戶端模組 API 的呼叫會傳回 200 回應碼。如果您回頭看 save 方法在用戶端中,您會發現其回呼前往 ninjaList。因此更新已成功地公佈,使用者會看見忍原始清單頁面。任何對剛變更忍者將會顯示該清單中。

我們有了忍尚未?

此解決方案會擲回我到 abyss 困難且寄不出信件結束。雖然我核心的目標是要使 Aurelia 與 DocumentDB 資料庫,我也想要使用這些技術,利用其優點。也就是說,不必了解 JavaScript 世界事情太多了,管理節點安裝、 偵錯 Node.js 和更了解 DocumentDB 因為這樣可以使用 Visual Studio 程式碼第一次。雖然有許多其他您可能想要在即使像我的簡單範例中的事物,本文應可提供基本的了解的事情的運作方式從一端到另一個。

要牢記一個重點是,DocumentDB — 像任何的 NoSQL 資料庫,針對大量的資料。例如,在我的範例中使用不具成本效益的小小的位元的資料。若要瀏覽的功能,連接到資料庫,以及與資料互動,但大量的資料沒有必要的因此五個物件命名。


Julie Lerman是 Microsoft MVP、.net 和顧問 Vermont 山區中。您可以找到她針對資料存取和使用者群組和世界各地的研討會其他.NET 主題呈現。她的部落格網址 thedatafarm.com /blog 以及 Code First 和 DbContext 版本中,所有從 O'Reilly Media 是 「 程式設計 Entity framework 」。在 Twitter 上追蹤她: @julielerman 並查看她 Pluralsight 課程 juliel.me/PS 影片

感謝以下技術專家對本文的審閱: Patrick Walters
Patrick Walters 一直一部分絕佳 Aurelia 開發人員社群人員盡情享受提供戴官方 Aurelia gitter 通道上的問題。