BIでパックマンを作ったnebula.jsは、実務でこそ効く。Qlik標準の表を11通りのリッチUIにしてみた

Qlik標準の表、ちょっと地味じゃない?

パックマンをQlikで動かす、というバカな二部作をやり切った直後だったんですが(あれはあれで楽しかった)、その過程で「nebula.jsって、ふざけたことだけじゃなく真面目にすごいこともできるんじゃないか」と思い始めました。(というかそっちが本来の用途だろう)

というのも、Qlikの標準テーブル。機能は十分なんですが、見た目がどうしても業務っぽいというか、垢抜けない。セルの中に推移グラフを入れたい、数字の大小をバーで見せたい、もっとモダンなダッシュボードにしたい——そう思っても標準ではなかなか届かない。

nebula.jsで拡張機能を書けば、中身は普通のHTML/CSS/JSです。ということは、Qlikのデータを取ってきさえすれば、あとはWebの表現力そのままで好きなだけリッチにできるはず。やってみました。

結論から言うと、同じ売上データが11通りの見た目に化けました。しかもそのうちいくつかは常に動き続ける。以下、作り方の勘所と、実際の見た目を並べます。

目次

元々の見た目

まず、出発点。これがQlik標準テーブルで見た「元データ」です。

Monthを削った版

カテゴリ別・月別の売上を、Qlik標準の「テーブル」チャートでそのまま表示したもの。機能的には過不足ないけれど、見た目はご覧の通り”ザ・業務”。 (※ シート自体もあえてデフォルトのままにしています。本当はテーマや書式でもう少し綺麗にできるのですが、「標準のまま=Before」を分かりやすくするために、素の状態で置いています。この地味な表が、この後どこまで化けるか——という話です。)

大前提:nebulaはcanvasだけじゃない、DOMも自由

前回のパックマンはcanvasにゲームを描きましたが、表やダッシュボードUIはDOM(HTML+CSS)で作るのが正解です。canvasだとテキスト選択もスクロールもホバーも全部自前実装になって地獄。DOMなら全部ブラウザ任せ、CSSでいくらでもリッチにできる。

nebulaのuseElement()で、拡張が描画されるDOM要素が取れます。ここにinnerHTMLで好きなHTMLを流し込めばいい。つまりQlikオブジェクトの中に、自由なWebページを作れるわけです。

肝心なところ:Qlikのテーブルデータをどう取るか

ここだけは押さえておく価値があるので説明します。難しくないです。

Qlikからデータを取る器がハイパーキューブです。拡張機能のプロパティに「どの次元・どのメジャーが欲しいか」を宣言しておくと、Qlikが集計済みのデータを渡してくれます。今回は「カテゴリ別・月別の売上」が欲しいので、次元にCategoryMonth、メジャーにSum(Sales)を指定します。

qHyperCubeDef: {
  qDimensions: [
    { qDef: { qFieldDefs: ['Category'] } },
    { qDef: { qFieldDefs: ['Month'] } },
  ],
  qMeasures: [
    { qDef: { qDef: '=Sum(Sales)' } },
  ],
  qInitialDataFetch: [{ qTop: 0, qLeft: 0, qWidth: 3, qHeight: 200 }],
}

取れたデータは、layout.qHyperCube.qDataPages[0].qMatrix に行列(マトリクス)として入っています。各行が [Category, Month, Sum(Sales)] の順に並んでいて、値は row[n].qText(表示文字)や row[n].qNum(数値)で読めます。

const matrix = layout.qHyperCube.qDataPages[0].qMatrix;
const rows = matrix.map((r) => ({
  category: r[0].qText,
  month: r[1].qText,
  sales: r[2].qNum,
}));

ポイントは、集計はQlikに任せること。Sum(Sales)の計算はQlik側でやってくれるので、こちらは「整形して描くだけ」。ここから先は普通のJavaScriptの世界です。カテゴリごとにグルーピングして、年間合計・前月比・12ヶ月の推移配列を作れば、あとはどう見せるかは自由。

// カテゴリごとにまとめて、合計・前月比・推移を計算するだけ
const byCat = groupBy(rows, 'category');
// → 各カテゴリに { total, mom, series: [月次の値...] } を持たせる

取れてしまえば勝ちです。ここまでが「Qlik固有」の話で、これ以降はもうWebフロントエンドの領域。

セル内にスパークライン(推移のミニ折れ線)、年間売上の背後にデータバー、前月比の▲▼。まずは”端正で実用的”な方向。これだけでも「Qlik標準の表」とは別物に見える。

あとはCSSで頑張るだけ

データが手に入れば、見せ方は完全に自由です。ここからは「同じtotalseriesを、CSSとちょっとのSVG/JSでどう料理するか」の話。実装の主役はほぼCSS。いくつか例を挙げます。

セル内スパークラインは、各行のseries(12ヶ月の配列)を小さなSVGの<polyline>に変換するだけ。上昇なら緑、下降なら赤に色分けして、終点にドットを打つと”それっぽく”なります。

データバーは、total / maxTotal をそのまま width: ○% にして、セルの背後に絶対配置した<div>を敷くだけ。CSSのpositionwidthだけで「数字+背景バー」が作れます。

アニメーション@keyframesで。バーのwidthを0から目標値まで伸ばせばロード時にニュッと動くし、requestAnimationFramewidthsinで揺らせば常時呼吸するように動き続ける。パックマンで使ったゲームループの技術が、ここでも効きました。

黒地に発光するシアンのバー、金銀銅のランク表示。同じデータを”電光掲示板”に。

「静止しない」ダッシュボードも作れる

ロード時に動いて止まる、では物足りなかったので、常に動き続ける版も作りました。requestAnimationFrameのループを回し続けて、バーを微妙に伸縮させたり、インジケータをパルス発光させたり。監視室のライブ画面みたいな”生きてる”感じになります。

ついでに「これ本当に売上データ?」ってなるやつも。背景が色相回転し、レインボーが流れ、文字がグローするやつ。BIダッシュボードというよりクラブのVJ画面。CSSのbackground-clip: textとグラデーションアニメだけでここまでいけます。

表・グラフの”形”そのものを変える

横棒と表ばかりだったので、形自体が違うものも。全部同じtotalseriesから作っています。

ハマったポイント(毎度おなじみ)

真面目なところで2つ。

外部フォントが使えない。 リッチにするならおしゃれなWebフォントを…と思うところですが、Qlik(CSP)は外部CDNからのリソース取得をブロックします。なのでGoogle Fontsは基本使えない。システムフォントスタック(system-uiや等幅のui-monospace)で組むのが無難です。数字はfont-variant-numeric: tabular-numsで等幅化すると、表がピシッと揃って一気に垢抜けます。ここは覚えておくと得。

プロパティを変えても反映されない。 これはパックマン二部作でも踏んだやつですが、開発プレビューの「Enable property cache」がオンだと、object-properties.jsのハイパーキューブ定義を変えても古い定義が使い回されます。データ構造をいじったら、このチェックを外してリロード。

まとめ:データさえ取れれば、あとはWebの表現力そのまま

やってみて改めて思ったのは、nebula.jsの本質は「Qlikのデータを、自由なWeb技術の世界に持ち出せる」ことだということ。ハイパーキューブでデータを取る——ここだけがQlik固有で、あとはHTML/CSS/JSの腕次第。表現の天井は、Qlikの標準チャートじゃなく、Webフロントエンドの天井まで上がる。

同じ売上データが11通りに化けたのが何よりの証拠です。実務なら、標準チャートで物足りない場面——「セル内にトレンドを見せたい」「ブランドに合わせた作り込みをしたい」——で、この手が効きます。パックマンは業務で使わないけど、これは明日使える。

もっとも、凝りすぎると保守が大変なのと、CSSのclass名がQlikのアップデートで…みたいな話は付いて回るので、そこはほどほどに。まずは「セル内スパークライン付きの実用テーブル」あたりから始めるのがおすすめです。

次回予告:で、これクリックできるの?

……と、いい感じにまとめたところで、書きながらずっと引っかかっていたことを白状します。

今回作った11パターン、全部ただの絵です。

クリックしても何も起きない。ハイパーキューブでデータを受け取って、描いて、終わり。よく考えたらこれ、Qlikのデータを使って絵を描いただけで、BIのチャートとしては何もしていないんですよね。

Qlikの本当の売りって、見た目じゃないはずです。クリックしたら選択されて、他のチャートも一斉に絞り込まれる。カテゴリを選んだら商品に降りていく。あの連想エンジンの動きこそがQlikで、標準チャートなら当たり前にやってくれることです。

じゃあ、自作のnebula.jsチャートでも、あれは再現できるのか。

  • 行をクリックして選択できる?
  • 選択したら、同じシートの他のオブジェクトも絞り込まれる
  • カテゴリ→商品とドリルダウンしていける?

ドキュメントを眺めた限りuseSelectionsみたいなAPIはあるので、たぶんできるんだと思います。ただ、この連載で「たぶんできる」と言った時の的中率をご存知の方はお察しの通りでして。だいたい実機で殴られます。

というわけで後編は「見た目」から「動き」へ。今回作った絵に、Qlikの魂を入れられるか試します。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

目次