はじめに

前回の「Qlik Senseエクステンション開発入門(1)- “Hello World”の作成」では文字列を表示するシンプルなエクステンションの作成の手順や、エクステンション・プロジェクトに含まれるファイルの役割などについて確認してきました。このエントリでは、Qlik Senseエンジンで集計されたデータをエクステンションでテーブル形式で表示する手順を見ていきたいと思います。

image

デバッグの方法

まずビジュアライゼーションの実装に入る前に、その実装に必要となる開発でのデバッグ方法について確認したいと思います。ここではChromeのWeb Developer Toolsを使った方法をご紹介します。

コンソールログでの変数の値の確認

console.logを利用することで、パラメータに設定された値などを参照することができます。

ここではpaintメソッドに以下の追加してみます。

		paint : function($element, layout) {

			//layoutをデバッグコンソールに表示
			console.log(layout);
          
			//add your rendering code here          
			$element.html( '<div id="display"></div>' );
            		$("#display").text("Displayed by jquery");
		}

layoutには、オブジェクト全体のレイアウト情報が含まれており、ここではその内容をconsole.logで参照します。ブラウザ上にエクステンションを配置し[F12]をクリックしてWeb Developer Toolsを表示し、「Console」タグを選択します。コンソールに以下の様なログが表示されていることを確認します。

image

この様に、変数に格納されている値を適時確認しながら開発を進めていくことができます。また、Web Developer Toolsではconsole.table()やconsole.group()などの便利なコマンドも用意されています。それらの情報については以下をご参照ください。

Using the Console

HTML要素の確認

エクステンションのレイアウトやスタイルなどを確認するためにHTML要素を確認することができます。[F12]をクリックして表示したWeb Developer Tools上で「Element」タグを選択開くとDOM(Document Object Model)を参照することができ、その中でHTMLの各要素にアクセスして適用されているCSSなどを確認することができます。

image

ビジュアライゼーションの実装

以上までがエクステンションの基本的な表示と動きについて確認してきました。次に、Qlik Senseアプリに含まれるデータをシンプルなテーブル形式で表示する処理の実装を通じて、Qlik Senseエクステンションのプロパティやデータの扱い方法を見ていきたいと思います。

JavaScriptファイルに含まれるエクステンションを実行するコードは大きく「initialProperties」、「definition」、「snapshot」、「paint」の4つのセグメントに分かれます。これらを一つ一つご説明します。

image_thumb[55]

initialProperties

initialPropertiesには、オブジェクトが最初に生成される際のプロパティを指定します。Qlik Senseエンジンの機能を使って集計されたデータをエクステンションを通じてビジュアル化する形になりますが、データへは以下いずれかのオブジェクトを介してアクセスする形となります。

  • ハイパーキューブ(qHyperCubeDef): 例えば「顧客名」、「商品カテゴリ」の軸と、「売上高」メジャーといった形で、複数の軸とメジャーを保持する多次元分析キューブのオブジェクト
  • リストオブジェクト(qListObjectDef): 例えば「支店名」を選択する選択ボックスの形で、単一項目のデータのみを保持するオブジェクト

これらの定義をこのinitialPropertiesで行います。ここでは、以下のinitialPropertiesの定義を追加してハイパーキューブの定義を行います。

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

上記では、versionは任意のバージョン情報を記載するユーザー定義プロパティになります。一方、qHyperCubeDefはQlikエンジン定義のプロパティとなり、ここに軸やメジャーを定義します。qWidthとqHeightにはフェッチするデータのセル数(列数×行数)の最大値を指定します。

definition

definitionには、プロパティパネルに表示するアイテムを指定します。プロパティには標準プロパティとカスタムプロパティが用意されています。

標準プロパティ

標準では以下のプロパティのアイテムが用意されています。

  • 軸(dimensions)
  • メジャー(measures)
  • ソート(sorting)
  • 拡張機能(addons)
  • スタイル(appearance)
  • 基本設定(settings)

ここでは、以下のdefinitionの定義を追加します。軸とメジャーをそれぞれ最大1個追加可能で、上記の一通りのアイテムを指定しています。

		definition: {
			type: "items",
			component: "accordion",
			items: {
				dimensions: {
					uses: "dimensions",
					max: 1
				},
				measures: {
					uses: "measures",
					max: 1
				},
				sorting: {
					uses: "sorting"
				},
              			addons: {
					uses: "addons"
				},
				settings: {
					uses: "settings"
				}
			}
		},

そうすると、以下の様にエクステンションのプロパティパネルに以下の設定項目が追加されます。

image_thumb[63]

カスタムプロパティ

また、カスタム・プロパティを追加することも可能です。カスタム・プロパティには以下の指定を行います。

  • ref: カスタム・プロパティを参照するためのID
  • label: プロパティパネル上に表示さえるラベル
  • type: string, integer, number, booleanのタイプを指定
  • defaultValue: プロパティの初期値を指定

以下の形でカスタムプロパティを追加します。

      	definition: {
			type: "items",
			component: "accordion",
			items: {
				dimensions: {
					uses: "dimensions",
					max: 1
				},
				measures: {
					uses: "measures",
					max: 1
				},
				sorting: {
					uses: "sorting"
				},
              	addons: {
					uses: "addons"
				},
				settings: {
					uses: "settings",
				  	items: {
				  		myNewHeader: {
            					type: "items",
            					label: "カスタムヘッダー",
							items: {
								MyStringProp: {
									ref: "prop.myTextBox",
									label: "カスタムプロパティ",
									type: "string",
									defaultValue: "MyExtension"
								}
							}
						}
					}
				}
			}
		},

そうすると、以下の形でプロパティが追加されます。プロパティはaccordion-section-header-itemの階層構造となっており、上記ではheaderと最下層のitemの2階層を追加しています。

image

また上記例はテキストボックスを追加していますが、componentの指定を行って以下の形式で追加することも可能です。

  • チェックボックス
  • テキストボックス
  • ドロップダウンリスト
  • ラジオボタン
  • ボタングループ
  • スイッチ
  • スライダー
  • レンジスライダー

上記設定方法の詳細については以下のURLをご参照ください。

プロパティの値参照

プロパティの値はpaintメソッド内でlayout.[property名(refに設定した値)]という形でアクセスすることが出来ます。

例えば、以下の行をpaintメソッドに追加すると、エクステンションのタイトルに設定した値と、先ほど追加したカスタムプロパティに設定した値がコンソールログに表示されます。

		paint : function($element,layout) {

			//プロパティをデバッグコンソールに表示
			console.log(layout.title);
			console.log(layout.prop.myTextBox);
		          
			//add your rendering code here          
			$element.html( '<div id="display"></div>' );
            		$("#display").text("Displayed by jquery");
		}

snapshot

snapshotでは、スナップショットを取得可能とするかを設定します。

		snapshot: {
			canTakeSnapshot: true
		},

上記の形でcanTakeSnapshotにtrueを設定すると、以下の形でスナップショットを取ってストーリーテリングなどに貼り付けて利用することができます。

image_thumb[87]

paint

paintメソッド内に、ビジュアライゼーションの主な処理の実装を行います。ここではまず、先ほどdefinitionで定義したハイパーキューブのデータを取得する処理を行います。

ここでは、エクステンションシートに配置し、以下の軸とメジャーを設定します。そうすると、内部的にこれらを軸とメジャーとしたハイパーキューブのオブジェクトが作成される形となります。

  • 軸:「Region」
  • メジャー:「Sum(Sales Amount)」

image_thumb[70]

そして、「デバッグの方法」の章でpaintメソッドの引数として与えられるlayoutはオブジェクト全体のレイアウト情報が含まれているとご説明しましたが、ハイパーキューブのデータ取得を行うには以下のパスを利用します。

  • layout.qHyperCube.qDimensionInfo : ディメンションの情報。qFallbackTitleにディメンション名を含む。
  • layout.qHyperCube.qMeasureInfo : メジャーの情報。qFallbackTitleにメジャー名を含む。
  • layout.qHyperCube.qDataPages: 結果データの情報。qMatrixにデータ配列を含む。

では、まずデータ配列を取得するために以下をpaintメソッドに追加します。

		paint : function($element,layout) {

			// qMatrixのデータ配列を取得
			var qMatrix = layout.qHyperCube.qDataPages[0].qMatrix;
          
			//add your rendering code here          
			$element.html( '<div id="display"></div>' );
            		$("#display").text("Displayed by jquery");
		}

console.log(qMatrix)で取得したデータ配列の値を確認すると、以下の形の配列データとなっていることが分かります。

image

配列の中身を確認してみると、それぞれ先ほど設定した「Region」軸と、集計された「Sum(Sales Amount)」の値が格納されていることが確認できます。

image

qMatrixはこのままのデータ形式では少し扱いにくいため、underscore.jsのmapメソッドを使って軸とメジャーをグループ化したオブジェクトを作成します。

			var data = qMatrix.map(function(d) {
				// for each element in the matrix, create a new object that has a property
				// for the grouping dimension(s), and the metric(s)
				return {
					"Dim1":d[0].qText,
					"Metric1":d[1].qNum
				}
			});

console.log(data)でこのデータの中身を確認すると、以下の形で軸とメジャーをグループ化されたことが確認できます。

image

そして、以下の形で軸とメジャーのラベルを取得します。

			// 軸のラベルを取得
			var dimensionLabels = layout.qHyperCube.qDimensionInfo.map(function(d) {
				return d.qFallbackTitle;
			});
		  
		    // メジャーのラベルを取得
		  	var measureLabels = layout.qHyperCube.qMeasureInfo.map(function(d) {
				return d.qFallbackTitle;
			});

console.logで中身を確認すると、以下の形で軸とメジャーのラベル名が取得されていることが確認できます。

image

以上で必要なデータの実体と、軸とメジャーのラベルの取得は完了しました。

では次に、これらのデータを使ってビジュアライゼーションの描画を行いたいと思います。まずは、以下の処理で一旦描画をクリアします。paintメソッドは、例えばエクステンションの表示サイズを変更するなどのレンダリング処理の際に毎回呼び出されますので、この処理を行わないとビジュアライゼーションが上書きされずに毎回追加されてしまいます。ビジュアライゼーションが描画される領域のHTML要素を含むjQueryラッパーである$elementに対してempty()処理で空にする処理を行っています。

		  	// 表示の初期化
		   	$element.empty();

そして、テーブルの描画処理を行います。先ほど取得した軸とメジャーのラベルを利用して以下の形でテーブルのヘッダーを描画します。

			// テーブルヘッダーの表示
		  	var html = '<table border="1"><thead><tr>';
			html += '<th' + " class='dimh'>" +  dimensionLabels + '</th>';
			html += '<th' + " class='meth'>" +  measureLabels + '</th>';	
			html += "</tr></thead><tbody>";

続いて、以下の処理を行い、テーブルの内容を表示します。変数のdataにはqMartixのデータ配列が格納されており、配列に含まれるDim1とMetric1をループで表示する処理を行っています。

			// テーブル内容の表示
			for (i = 0; i < data.length; i++) {		
				html += '<tr>';		
				html += '<td';
				html += " class='text'>" + data[i].Dim1 +  '</td>';
				html += '<td';
				html += " class='numeric'>"+ data[i].Metric1 + '</td>';				
				html += '</tr>';		
			}	
			html += "</tbody></table>";	

最後に描画の実行処理部分を修正します。現状では以下の形で定型の文字列を表示する処理となっています。

			//add your rendering code here          
			$element.html( '<div id="display"></div>' );
            		$("#display").text("Displayed by jquery");

この部分を以下の形に修正します。変数のhtmlには表示するテーブルのHTML要素が含まれており、それをビジュアライゼーションが描画される領域に表示する処理を行います。

			//add your rendering code here          
			$element.html( html );

以上のコードの実装を行い、エクステンションを表示すると以下のビジュアライゼーションが表示されます。

image

尚、作成されたJavascriptの全体は以下となります。

define( [
  'jquery',
  'text!./style.css'
],
function ($, cssContent) {
    $("<style>").html(cssContent).appendTo("head");
    //$('<link rel="stylesheet" type="text/css" href="/extensions/MyExtension/style.css"  />').appendTo("head");

	return {
      	initialProperties: {
			version: 1.0,
			qHyperCubeDef: {
				qDimensions: [],
				qMeasures: [],
				qInitialDataFetch: [{
					qWidth: 2,
					qHeight: 50
				}]
			}
		},
      	definition: {
			type: "items",
			component: "accordion",
			items: {
				dimensions: {
					uses: "dimensions",
					max: 1
				},
				measures: {
					uses: "measures",
					max: 1
				},
				sorting: {
					uses: "sorting"
				},
              	addons: {
					uses: "addons"
				},
				settings: {
					uses: "settings",
				  	items: {
				  		myNewHeader: {
            				type: "items",
            				label: "カスタムヘッダー",
							items: {
								MyStringProp: {
									ref: "prop.myTextBox",
									label: "カスタムプロパティ",
									type: "string",
									defaultValue: "MyExtension"
								}
							}
						}
					}
				}
			}
		},
      	snapshot: {
			canTakeSnapshot: true
		},
		paint : function($element,layout) {

			// qMatrixのデータ配列を取得
			var qMatrix = layout.qHyperCube.qDataPages[0].qMatrix;
		  
		  	var data = qMatrix.map(function(d) {
				return {
					"Dim1":d[0].qText,
					"Metric1":d[1].qNum
				}
			});

			// 軸のラベルを取得
			var dimensionLabels = layout.qHyperCube.qDimensionInfo.map(function(d) {
				return d.qFallbackTitle;
			});
		  
		    // メジャーのラベルを取得
		  	var measureLabels = layout.qHyperCube.qMeasureInfo.map(function(d) {
				return d.qFallbackTitle;
			});
		  
		  	// 表示の初期化
		   	$element.empty();
		  
			// テーブルヘッダーの表示
		  	var html = '<table border="1"><thead><tr>';
			html += '<th' + " class='dimh'>" +  dimensionLabels + '</th>';
			html += '<th' + " class='meth'>" +  measureLabels + '</th>';	
			html += "</tr></thead><tbody>";
		  
		  	// テーブル内容の表示
			for (i = 0; i < data.length; i++) {		
				html += '<tr>';		
				html += '<td';
				html += " class='text'>" + data[i].Dim1 +  '</td>';
				html += '<td';
				html += " class='numeric'>"+ data[i].Metric1 + '</td>';				
				html += '</tr>';		
			}	
			html += "</tbody></table>";	
		  
			//add your rendering code here          
			$element.html( html );
		}
	};

} );

まとめ

このエントリでは、テーブル形式でデータを表示するエクステンションの実装を通じて、Qlik Senseエンジンで集計されたデータを取得方法と、ビジュアライゼーションの基本的な手順についてご説明しました。このエントリで作成したテーブルはスクロール機能などが実装されておらず、またスタイルも適用されていませんが、ここでご紹介した基礎を元にHTML5, CSS, jQuery, AngularJSなどを活用することでさらに凝ったビジュアライゼーションを実装することができます。

>>Qlik Senseエクステンション開発入門(3)-propertyとinitialPropertyのファイル分割