Angular と Connect Server を使用した関連テーブルのデータ表示
Angular は、Angular JS の原則に基づいて構築・拡張された、動的な Web アプリ向けの最新フレームワークです。CData Connect Server を使用すると、250 以上のデータソース(オンプレミスおよびクラウドベースのデータベースを含む)に対して REST API を生成できます。この記事では、CData Connect Server をセットアップして QuickBooks Online データ用の OData ベース REST API を作成し、QuickBooks Online データにライブアクセスできるシンプルなシングルページアプリケーション(SPA)を構築する手順を解説します。この SPA は、関連する QuickBooks Online テーブル(Invoices と Invoice Line Items など)に基づいて、HTML テーブルを動的に構築・表示します。この記事では主要なコードを順を追って説明しますが、サンプル Angular プロジェクトをダウンロードして、完全なソースコードの確認や機能のテストを行うこともできます。
Connect Server のセットアップ
まだインストールしていない場合は、CData Connect Server をダウンロードしてください。Connect Server をインストールしたら、アプリケーションを起動し、データへの接続を設定します(この記事では付属のサンプルデータベースを使用します)。次に、SPA でアクセスしたいテーブル用の REST API を作成するようにアプリケーションを設定します。
CORS の有効化
Angular Web アプリと Connect Server が異なるドメインにある場合、Angular アプリはクロスドメインリクエストを生成します。そのため、Angular Web アプリからクエリされるサーバーでは CORS(クロスオリジンリソース共有)を有効にする必要があります。Connect Server で CORS を有効にするには、Connect Server の SETTINGS ページにある Server タブに移動し、以下の設定を調整します。
- 「Enable cross-origin resource sharing (CORS)」のチェックボックスをオンにします。
- 「Allow all domains without '*'」のチェックボックスをオンにするか、Access-Control-Allow-Origin に接続を許可するドメインを指定します。
- Access-Control-Allow-Methods を「GET,PUT,POST,OPTIONS」に設定します。
- Access-Control-Allow-Headers を「authorization」に設定します。
- Save Changes をクリックします。
QuickBooks Online への接続
デプロイ後、Connect Server 管理コンソールで Settings -> Connections をクリックし、新しい接続を追加して、QuickBooks Online への接続に必要な認証値やその他の接続プロパティを指定します。
QuickBooks Online は OAuth 認証標準を使用します。OAuth では、認証ユーザーがブラウザを通じてログインする必要があります。OAuth を使用して認証するには、組み込みの OAuthClientId、OAuthClientSecret、CallbackURL を使用するか、Intuit でアプリを登録して独自の値を取得します。また、CompanyId も指定する必要があります。
OAuth の使用方法については、ヘルプドキュメントの「はじめに」の章を参照してください。

ユーザーの設定
次に、Connect Server を通じてデータベースデータにアクセスするためのユーザーを作成します。USERS タブでユーザーを追加・設定できます。ここではデータ閲覧用のシンプルな SPA を作成するため、読み取り専用アクセス権を持つユーザーを作成します。 Add をクリックし、ユーザー名を入力して Save Changes をクリックします。

スクリーンショットに示されているように、読み取りおよび書き込みアクセス権を持つユーザーが既に設定されています。この記事では、読み取り専用ユーザーと、その関連する authtoken を使用して Connect Server にアクセスします。

テーブルへのアクセス
ユーザーを作成したら、データベーステーブルへのアクセスを有効にします。テーブルを有効にするには、ODATA タブに移動して Add Tables をクリックします。アクセスしたいデータ接続を選択し、 Next をクリックします。接続を選択した状態で、テーブル名をクリックして Next を選択することでリソースの有効化を開始できます。リソースは一度に1つのテーブルずつ追加する必要があります。この例では、すべてのテーブルを有効にしています。

REST API のサンプル URL
データベースへの接続を設定し、ユーザーを作成し、Connect Server にリソースを追加したことで、OData プロトコルに基づいた簡単にアクセスできる REST API が完成しました。以下に、テーブルとそれらにアクセスするための URL のリストを示します。テーブルへのアクセス方法については、ODATA ページにある API タブを参照してください。URL には Connect Server の address と port が必要です。Angular を使用しているため、デフォルトで JSON データを返さない URL には @json パラメータを末尾に追加します。
| テーブル | URL |
|---|---|
| エンティティ(テーブル)リスト | http://address:port/api.rsc/ |
| QBO_Invoices テーブルのメタデータ | http://address:port/api.rsc/QuickBooksOnlineConnection_Accounts/$metadata?@json |
| QBO_Invoices データ | http://address:port/api.rsc/QuickBooksOnlineConnection_Accounts |
標準の OData フィードと同様に、返されるフィールドを制限したい場合は、クエリに $select パラメータを追加できます。また、$filter、$orderby、$skip、$top などの標準 URL パラメータも使用できます。
シングルページアプリケーションの構築
Connect Server のセットアップが完了したら、SPA を構築していきましょう。.zip ファイルに含まれる SPA のソースファイルを順を追って説明し、重要なコードセクションについて解説します。いくつかのソースファイルは、angular.io の Angular チュートリアルをベースにしています。
src/index.html
これは SPA のホームページで、ソースコードは主に必要な Angular ライブラリをインポートするための script 要素で構成されています。
src/main.ts
この TypeScript ファイルはアプリをブートストラップします。
src/app/app.module.ts
この TypeScript ファイルは、SPA を作成・実行するために必要なモジュールをインポートするクラスを作成します。このクラスには、コンポーネントとサービスの定義が含まれています。
src/app/app-routing.module.ts
この TypeScript ファイルは、SPA のコンテンツ間を移動するためのルートとパスを定義します。
src/app/app.component.css
このファイルは、Web アプリ内の h1 要素と h2 要素を変更する CSS ルールセットを作成します。
src/app/app.component.ts
この TypeScript ファイルは、SPA 用のコンポーネントを作成し、テンプレートを定義します。このアプリはシンプルですが、複数のルーティングやコンポーネントを含むように容易に拡張できます。
src/app/dashboard.component.css
このファイルは、HTML 内の table、th、td 要素を変更する CSS ルールセットを作成します。
src/app/dashboard.component.html
このファイルは、SPA のダッシュボードコンポーネントのレイアウトを定義します。テンプレートには、テーブルと関連テーブルを選択するドロップダウン、テーブルの外部キーを示すカラムを選択するドロップダウン、親テーブルデータを表示する HTML テーブル、子テーブルデータを表示する別の HTML テーブルが含まれています。*ngIf ディレクティブの条件に基づいてセクションの表示/非表示が切り替わり、*ngFor ディレクティブを使用して Connect Server への呼び出し結果をループ処理することで、メニューとテーブルが動的に構築されます。

Connect Server へのすべての呼び出しと変数への値の割り当ては、DashboardComponent クラスと AppService クラスで行われます。
<div style='float:left' class="table_select">
<label>Select a Table</label>
<br>
<select [(ngModel)]="selectedTable" (change)="tableChanged()">
<option *ngFor="let sel_table of tableNames" [value]="sel_table">{{sel_table}}</option>
</select>
<br>
<div *ngIf="selectedTable">
<label>Select the Key for [{{selectedTable}}]</label>
<br>
<select [(ngModel)]="tableKey">
<option *ngFor="let sel_column of tableColumns" [value]="sel_column">{{sel_column}}</option>
</select>
<br>
</div>
</div>
<div class="subtable_select" *ngIf="selectedTable">
<label>Select a SubTable</label>
<br>
<select [(ngModel)]="selectedSubTable" (change)="subTableChanged()">
<option *ngFor="let sel_table of tableNames" [value]="sel_table">{{sel_table}}</option>
</select>
<br>
<div *ngIf="selectedSubTable">
<label>Select the Key for [{{selectedSubTable}}]</label>
<br>
<select *ngIf="selectedSubTable" [(ngModel)]="subTableKey">
<option *ngFor="let sel_column of subTableColumns" [value]="sel_column">{{sel_column}}</option>
</select>
<br>
</div>
</div>
<div *ngIf="selectedTable && tableKey && selectedSubTable && subTableKey && tableData?.length > 0" class="data_retrieve">
<br>
<h2>Click an Entry from [{{selectedTable}}] to Expand the [{{selectedSubTable}}] Entities</h2>
<table>
<tr>
<th *ngFor="let column of tableColumns">{{ column }}</th>
</tr>
<tr style='cursor:pointer' *ngFor="let row of tableData" (click)="rowClicked(row[tableKey])">
<td *ngFor="let column of tableColumns">{{ row[column] }}</td>
</tr>
</table>
</div>
<div *ngIf="selectedSubTable && subTableColumns && subTableData?.length > 0">
<br>
<hr>
<h2>Data from [{{selectedSubTable}}]</h2>
<table>
<tr>
<th *ngFor="let column of subTableColumns">{{ column }}</th>
</tr>
<tr *ngFor="let row of subTableData">
<td align=center *ngFor="let column of subTableColumns">{{ row[column] }}</td>
</tr>
</table>
</div>
src/app/app.service.ts
この TypeScript ファイルは、Connect Server からデータを取得するためのサービスを構築します。テーブルリストの取得、特定のテーブルのカラムリストの取得、テーブルからのデータ取得といった関数が含まれています。また、Connect Server が返すテーブルのメタデータを表すクラスも定義されています。
API_Table
Connect Server がテーブルに対して返すメタデータには、テーブルの name、kind、URL が含まれます。ここでは name フィールドのみを使用しますが、SPA を拡張する際に他の情報が必要になる場合に備えて、オブジェクト全体を渡しています。
export class API_Table {
name: string;
kind: string;
url: string;
}
constructor()
コンストラクターでは、Http クラスのプライベートインスタンスを作成し、先ほど作成したユーザーの user/authtoken 資格情報に基づいて Authorization HTTP ヘッダーを設定します。このヘッダーを HTTP リクエストに含めます。
constructor(private http: Http) {
this.headers.append('Authorization', 'Basic ' + btoa(this.userName+":"+this.authToken));
}
getTables()
この関数はテーブルのリストを返します。リストは、Authorization ヘッダーを含む HTTP GET リクエストを Connect Server のベース URL(http://localhost:8080/odata.rsc)に送信することで取得されます。
getTables(): Promise<API_Table[]> {
return this.http.get(this.baseUrl, {headers: this.headers})
.toPromise()
.then(response => response.json().value )
.catch(this.handleError);
}
getColumns()
この関数は、tableName で指定されたテーブルのカラムリストを返します。$metadata エンドポイントはデフォルトで XML 形式のデータを返すため、Connect Server から JSON データを確実に取得するために URL に @json パラメータを渡します。JSON データを取得したら、ドリルダウンしてカラム名のリストを取得できます。
getColumns(tableName: string): Promise<string[]> {
return this.http.get(`${this.baseUrl}/${tableName}/$metadata?@json`, {headers: this.headers})
.toPromise()
.then(response => response = response.json().items[0]["odata:cname"] )
.catch(this.handleError);
}
getTableDataByColumns(tableName:string, columnList: string)
この関数は、指定されたテーブルとカラムのデータ行を返します。URL に tableName を渡し、カラムのリスト(カンマ区切りの文字列)を $select URL パラメータの値として渡します。特定のカラムが指定されていない場合は、$select URL パラメータを使用せずにすべてのカラムをリクエストします。
getTableDataByColumns(tableName:string, columnList: string): Promise<Object[]>
{
if (columnList) {
return this.http.get(`${this.baseUrl}/${tableName}/?$select=${columnList}`, {headers: this.headers})
.toPromise()
.then(response => response = response.json().value )
.catch(this.handleError);
} else {
return this.http.get(`${this.baseUrl}/${tableName}/`, {headers: this.headers})
.toPromise()
.then(response => response = response.json().value )
.catch(this.handleError);
}
}
getAllTableDataById(tableName:string, idColumn:string, idValue:string)
この関数は、指定された ID カラムと値に基づいて、指定されたテーブルのデータ行を返します。URL に tableName を渡し、ID カラムと値を使用してメインテーブルの特定のエントリに関連するデータをリクエストします。
getAllTableDataById(tableName:string, idColumn:string, idValue:string): Promise<Object[]>
{
return this.http.get(`${this.baseUrl}/${tableName}(${idColumn}='${idValue}')`, {headers: this.headers})
.toPromise()
.then(response => response = JSON.parse('[' + response['_body'] + ']'))
.catch(this.handleError);
}
src/app/dashboard.component.ts
この TypeScript ファイルでは、SPA のイベントに反応する関数を定義しています。これらの関数内で AppService の関数を呼び出し、その結果を使用して SPA のさまざまな要素を表示します。これらの関数は比較的単純で、必要に応じて異なる変数に値を割り当てています。
ngOnInit()
この関数では、AppService の getTables 関数を呼び出します。getTables は Connect Server テーブルクエリから生の data オブジェクトを返すため、各結果からオブジェクト全体ではなく name フィールドのみを利用可能なテーブルの配列にプッシュする必要があります。
ngOnInit(): void {
this.appService
.getTables()
.then( tables => {
for (let tableObj of tables) {
this.tableNames.push( tableObj.name )
}
});
}
tableChanged()
この関数は、ユーザーが SPA のドロップダウンメニューからテーブルを選択するたびに呼び出されます。この関数は Connect Server を呼び出して指定されたテーブルのカラムリストを取得し、別のドロップダウンメニューに表示します。また、選択されたテーブルのデータも取得し、HTML テーブルに表示します。
tableChanged(): void {
this.appService
.getColumns(this.selectedTable)
.then( columns => this.tableColumns = columns.sort() );
this.appService
.getTableDataByColumns(this.selectedTable, this.tableColumns)
.then( data => this.tableData = data );
}
subTableChanged()
この関数は、ユーザーがドロップダウンメニューから関連テーブルを選択するたびに呼び出されます。この関数は Connect Server を呼び出して指定されたテーブルのカラムリストを取得し、別のドロップダウンメニューに表示します。
subTableChanged(): void {
this.appService
.getColumns(this.selectedSubTable)
.then( columns => this.subTableColumns = columns.sort() );
}
rowClicked(keyValue: string)
この関数は、メインテーブルのデータ行がクリックされるたびに呼び出されます。メインテーブルで選択されたカラムに基づいて行の ID 値をキャプチャし、選択された ID に基づいて関連テーブルからデータを取得するために Connect Server を呼び出します。取得されたデータは HTML テーブルに表示されます。
rowClicked(keyValue: string): void {
columnList = this.selectedColumns.join(',');
this.appService
.getTableData( this.selectedTable, columnList )
.then( data => this.tableData = data );
}
シングルページアプリケーションの実行
データへの接続を設定し、SPA のソースファイルを確認したら、シングルページアプリケーションを実行する準備が整いました。SPA を実行するには、マシンに node.js と npm がインストールされている必要があります。サンプルダウンロードには、設定済みの package.json ファイルが含まれています。SPA のルートディレクトリでコマンドラインから npm install を実行することで、必要なモジュールをインストールできます。SPA を起動するには、同じディレクトリで npm start を実行します。
SPA が起動すると、タイトルとテーブルを選択するドロップダウンメニューが表示されます。テーブルのリストは Connect Server から取得され、Connect Server の設定時にリソースとして追加したすべてのテーブルが含まれています。

テーブルを選択すると、カラムのドロップダウンが表示され、テーブルとサブテーブルを関連付けるキーカラムを選択できます。

メインテーブルとキーカラムを選択したら、関連するサブテーブルを選択できます。

サブテーブルを選択すると、カラムのドロップダウンが表示され、テーブルとサブテーブルを関連付けるキーカラムを選択できます。

テーブルとカラムを選択すると、メインテーブルのデータが表示されます。HTML テーブルの行をクリックすると、クリックしたエントリに対応する関連ラインアイテムをサブテーブルから取得できます。

無料トライアルと詳細情報
動的な Web ページでデータベースデータに接続する基本的な例をご覧いただきました。Connect Server ページで詳細情報を確認し、Connect Server をダウンロードしてください。QuickBooks Online などのオンプレミスおよびクラウドベースのデータベース、アプリケーション、サービスからのライブデータを使用して、動的な Web ページの構築を始めましょう。ご不明な点がございましたら、サポートチームがお手伝いいたします。