はじめに

「Qlik Senseエクステンション開発入門(2)- 基本的なテーブルの作成」ではテーブル表示を行うエクステンションを開発しました。ただ、このエクステンションにの利用に当たってはデータページ(DataPage)について注意する必要があります。

作成したエクステンションの「initialProperties」の「qInitialDataFetch」の設定を見てみましょう。

initialProperties: {
			version: 1.0,
			qHyperCubeDef: {
				qDimensions: [],
				qMeasures: [],
				qInitialDataFetch: [{
					qWidth: 2,
					qHeight: 50
				}]
			}
		},

ここで、qWidthqHeightにそれぞれ2と50という値が設定されています。これは、一度のフェッチ処理で取得するデータページのサイズを指しており、「最大2列、50行のデータを取得する」という設定をここで行っている形となっています。一度の呼び出しで巨大なデータセットの全体を取得するのは多くの場合処理として効率的ではないため、Qlik Senseではデータページに分けてデータを取得する機能が実装されています。(Qlik Senseでのページングの考え方についてはこちらをご参照ください。)

また、このデータページに指定できる上限値は「列数×行数=10,000」となっており、一度のフェッチ処理で最大1万セルまで取得が可能となっています。従って、このqWidthとqHightの設定値、もしくは1万セルの上限を超えるデータが存在する場合には、フェッチを繰り返し実行してデータ全体を取得する処理を実装する必要があります。

Backend APIを使うメリット

Backend APIはEngine APIの一部のメソッドをエクステンションやマッシュアップから呼び出し易くするよう、それらのメソッドのラッパーとして提供するAPIとなっており、例えば以下の様なメソッドが提供されています。(全てのBackendAPIのメソッドはこちらをご参照ください。)

  • clearSelections method
  • eachDataRow method
  • getData method
  • getDataRow method
  • getDimensionInfos method
  • getMeasureInfos method
  • getProperties method
  • getRowCount method
  • save method
  • search method
  • selectValues method
  • setProperties method

このBackend APIを利用すると、フェッチを繰り返し実行してデータ全体を取得する処理を実装することができます。Qlik Senseでは標準で「Table template」という名称のテンプレートが提供されていますが、このテンプレートではBackend APIを利用したこの処理が実装されており、以下の形で一度のフェッチで全体のデータが取得できない場合には「More」のボタンをクリックして追加のデータを表示することができます。

image

このエントリでは、この「Table template」を取り上げて、その実装内容について解説を行いたいと思います。

エクステンション作成の準備

ウェブブラウザから以下のURLを開き、Dev Hubにアクセスします。(Chromeの利用が推奨されます。また、Dev HubはQlik Sense Desktopが稼働している場合のみ利用可能ですのでご注意ください。)

http://localhost:4848/dev-hub/

image

「Create New」をクリックします。

1

以下を入力・選択して「Create」をクリックします。

  • Name: MyTable
  • Template: Table template

 image

以下の編集画面が立ち上がります。

image

サンプルエクステンションの解説

initialProperties, definition, snapshotの設定

では、MyTable.jsの内容を見ていきたいと思います。まず、initialProperties, definition, snapshotの設定を行っています。

 

define( ["jquery", "text!./style.css"], function ( $, cssContent ) {
	'use strict';
	$( "<style>" ).html( cssContent ).appendTo( "head" );
	return {
		initialProperties: {
			qHyperCubeDef: {
				qDimensions: [],
				qMeasures: [],
				qInitialDataFetch: [{
					qWidth: 10,
					qHeight: 50
				}]
			}
		},
		definition: {
			type: "items",
			component: "accordion",
			items: {
				dimensions: {
					uses: "dimensions",
					min: 1
				},
				measures: {
					uses: "measures",
					min: 0
				},
				sorting: {
					uses: "sorting"
				},
				settings: {
					uses: "settings"
				}
			}
		},
		snapshot: {
			canTakeSnapshot: true
		},

これらについては「Qlik Senseエクステンション開発入門(2)- 基本的なテーブルの作成」で解説を行っていますのでそちらをご参照頂ければと思いますが、簡単にまとめると以下の指定を行っています。

  • initialProperties: 10列、50行のデータページのフェッチサイズを指定して、ハイパーキューブを作成
  • definition: 軸、メジャー、ソート、基本設定の4つの標準プロパティをプロパティパネルに表示するよう設定
  • snapshot: エクステンションのスナップショットを取得可能に設定

paintの実装

次にpaintメソッド内の実装を見ていきたいと思います。

まず、以下の4つの変数を作成しています。

  • html – エクステンションに表示するHTML要素を格納
  • self - エクステンション自身のインスタンスへの参照を格納
  • lastrow -表示済みの行の位置を格納
  • morebutton - 「More」ボタンの表示・非表示のフラグを格納
var html = "<table><thead><tr>", self = this, lastrow = 0, morebutton = false;

そして、「軸」と「メジャー」のラベルをテーブルのヘッダーに表示する処理を行っています。それを行うために、Backend APIのgetDimensionInfosgetMeasureInfosのメソッドを利用し、返されたデータの「qFallbackTitle」にそれぞれ「軸」と「メジャー」のラベルが格納されていますので、それらの値をテーブルのヘッダーに設定しています。

 

			//render titles
			this.backendApi.getDimensionInfos().forEach( function ( cell ) {
				html += '<th>' + cell.qFallbackTitle + '</th>';
			} );
			this.backendApi.getMeasureInfos().forEach( function ( cell ) {
				html += '<th>' + cell.qFallbackTitle + '</th>';
			} );
			html += "</tr></thead><tbody>";

次に、テーブルのデータ部分を表示する処理を行っています。backendAPIのeachDataRowメソッドにより、レコード行が返され、その中のrow.forEach処理により、各レコード行を複数の列のセルに分解してテーブルに格納しています。その際に以下の2つの処理を行っています。

  • qIsOtherCellにより「その他」として纏められた行かどうかを判別し、纏められた行の場合にはothersLabelに設定された「その他のラベル」を表示する
  • qNumにデータが格納されている場合には数値データと判断し、<tb>タグに’numeric’のクラスを追加する(style.cssのCSSファイル上で、numericクラスのセルはテキスト右寄せを行う指定が行われています。)
			//render data
			this.backendApi.eachDataRow( function ( rownum, row ) {
				lastrow = rownum;
				html += '<tr>';
				row.forEach( function ( cell, index  ) {
					if ( cell.qIsOtherCell ) {
						cell.qText = this.backendApi.getDimensionInfos()[index].othersLabel;
					}
					html += '<td';
					if ( !isNaN( cell.qNum ) ) {
						html += " class='numeric'";
					}
					html += '>' + cell.qText + '</td>';
				} );
				html += '</tr>';
			} );
			html += "</tbody></table>";

上記の処理の中で、取得された行数がlastrowに格納されています。そして、以下でbackend APIのgetRowCountで全体のレコード行数を取得して比較し、まだレコードが全て取得されていない場合には「More」ボタンを追加してmorebuttonのフラグにtrueを設定しています。

			//add 'more...' button
			if ( this.backendApi.getRowCount() > lastrow + 1 ) {
				html += "<button id='more'>More...</button>";
				morebutton = true;
			}
			$element.html( html );

そして最後にmorebuttonのフラグにtrueを設定されている場合には、「More」ボタンが押された時に実行されるイベントを以下の形で追加しています。

  • $element.find( "#more" ).on( "qv-activate", function () {・・

HTML要素から”more”のidを含む要素(先ほど追加したボタンにid=”more”が設定されています)を見つけて、イベントをバインドしています。そしてこのイベントの中でbackend APIのgetDataメソッドで追加のデータページが取得し、self.paint( $element )でエクステンションを再描画する処理を行っています。

			if ( morebutton ) {
				var requestPage = [{
					qTop: lastrow + 1,
					qLeft: 0,
					qWidth: 10, //should be # of columns
					qHeight: Math.min( 50, this.backendApi.getRowCount() - lastrow )
				}];
				$element.find( "#more" ).on( "qv-activate", function () {
					self.backendApi.getData( requestPage ).then( function ( /*dataPages*/ ) {
						self.paint( $element );
					} );
				} );
			}

CSSの設定

また、style.cssのCSSファイルでは以下の形で、テーブルの枠線などのスタイルの設定を行っています。ここで、「div.qv-object-content-container」に対して「overflow: auto;」の指定を行っています。これはエクステンションの枠内からテーブル全体が表示がはみ出した場合の表示方法の指定で、autoによりブラウザ依存の設定としていますが、一般的にはスクロールして全体が見られる形となります。

.qv-object-MyTable div.qv-object-content-container {
	overflow: auto;
}
.qv-object-MyTable td,
.qv-object-MyTable th {
	border-top: 0px solid #fff;
	border-bottom: 1px solid #ddd;
	border-right: 1px solid #ddd;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
	vertical-align: middle;
	cursor: default;
	font-size: 12px;
}
.qv-object-MyTable td.numeric {
	text-align: right;
}
.qv-object-MyTable button {
	width: 100%;
}

まとめ

以上、Qlik Senseでのデータページの考え方と、Backend APIを利用してフェッチを繰り返し実行してデータ全体を取得する処理を実装する方法についてご解説しました。また、以下では「senseUtils」というJavaScript Libraryが公開されており、この中のpageExtensionDataという関数で一度に1万セルを超えたデータを一度に取得することができます。(この関数の中では今回ご紹介したbackend APIが利用されています。)こういったライブラリを活用してDataPageを扱うこともできます。

senseUtils - a JavaScript library for developing Qlik Sense web solutions with the Sense APIs

>>Qlik Senseエクステンション開発入門(5)- AngularJSの活用