こんにちは。サーバーサイド開発から急遽クライアント側の開発をすることになりました。
未知の世界で苦労しているので、忘れない為にもこつこつと記録に残していきたいと思います。
ということで今回はタブ付きのGridを実現させてみたいと思います。
こんなの↓↓ です。
※Plunkerだとうまくいかなかったので画像で我慢してください。。。
なお、本記事はWindows環境での手順なので、他の環境については良い感じに読み替えて頂ければと思います
事前準備
まず、事前に必要なものたちは以下の通り。
- Node.js (typescript, bowerをインストールする為にnpmを使います)
- Typescript (今回はTypescriptで書きます)
- tsd (型定義ファイルをインストールするのに使います)
- Bower (angularJSもろもろをインストールするのに使います)
まずはこいつらを準備しましょう。
Node.js
既に導入済みの場合は飛ばしてください。
特別なことは特にないので、ココからDLしてインストールしましょう。
exeファイルを実行した後、ウィザードに沿ってサクッといけます。
ちなみに今回使用したのはv5.4.1 Stable
です。
Typescript
今回はTypescriptを使いたいので、npmでインストールします。
Typescript使わない方は無視してください。後ほどES5でコンパイルしたJSソースも展開します。
Node.jsをインストールすればnpmも自動で入ってますので、GitBashやらで以下のコマンドを実行します。
$ npm install typescript -g
tsd
Typescriptを使用するにあたって必要な型定義ファイル
を管理する為のツールです。
これもTypescriptを使わない方は無視してください。
インストールはnpmで行います。
$ npm install tsd -g
Bower
Bowerはクライアントサイド用のパッケージマネージャーです。
npmはサーバーサイド用として、それぞれ使い分けるのが一般的なようですが、
今回はそれに則ってangularjsやui-grid等のツールはBowerでインストールします。
$ npm install bower -g
Bowerとかnpmとかについてはこの記事が無茶苦茶わかりやすいです。
今回のプロジェクト構成(最終形)
今回はファイル数も多くないですし、めんどうなので男らしくフォルダ分割等をせずにいきたいと思います。
ui-grid-sample
┣ bower_components // Bowerでインストールされたパッケージたち
┣ typings // tsdでインストールされた型定義ファイルたち
┣ app.css
┣ app.js // コンパイル(ts->js)時に1つのjsとしてまとめています
┣ app.ts
┣ bower.json // Bowerの設定ファイル
┣ DataService.ts // データ処理担当クラス
┣ index.html
┣ MainController.ts // コントローラクラス
┣ tsconfig.json // tsc(Typescriptのコンパイラ)の設定ファイル
┗ tsd.json // tsdの設定ファイル
ルート直下に全部おきます。
tsファイルをコンパイルしたjsファイルも同じ場所におきました。
もろもろの説明
コードの説明だけを少し…。各種設定ファイルは今回は割愛させて頂きます。
コード一式と使い方
こちらに置きました。
※ところどころアクセス修飾子があったりなかったり、型注釈が抜けてたりしますが、ご容赦下さい…
Cloneしたら以下のコマンドを実行して依存パッケージと型定義ファイルをインストールしてください。
※以下GitBash想定
- プロジェクトルートに移動
$ cd {ui-grid-sampleの場所}
- Bowerで依存パッケージのインストール
$ bower install
上記コマンドを実行することでbower.json
に記述された依存パッケージがインストールされます。
(bower_componentディレクトリが作成されます)
- tsdで型定義ファイルのインストール
$ tsd install
上記コマンドを実行することでtsd.json
に記述された依存パッケージがインストールされます。
(typeingsディレクトリが作成されます)
この時点でindex.html
をFirefoxで開くと、以下のようなgridが出るんじゃないかなーと思います。
Chrome等ではそのままでは実行できないのでご注意ください。
どうしてもChrome等で実行したい場合はgulpでgulp-webserverプラグインを利用し、webserverタスクを作成すると良いでしょう。
index.htmlを基点に、コードを少し説明
<!DOCTYPE html>
<html ng-app="app">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>ui-grid + tab sample</title>
<link rel="stylesheet" href="bower_components/angular-ui-grid/ui-grid.css" type="text/css">
<link rel="stylesheet" href="bower_components/angular-bootstrap/ui-bootstrap-csp.css" type="text/css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
<link rel="stylesheet" href="app.css" type="text/css">
<script src="bower_components/angular/angular.js"></script>
<script src="bower_components/angular-ui-grid/ui-grid.js"></script>
<script src="bower_components/angular-bootstrap/ui-bootstrap-tpls.js"></script>
<!-- <script src="./DataService.js"></script>
<script src="./MainController.js"></script> -->
<script src="./app.js"></script>
</head>
<body>
<div ng-controller="MainController as mainController">
<input ng-model="mainController.filterValue" ng-change="mainController.update()" placeholder="名前検索"/>
<br/><br/>
<uib-tabset>
<uib-tab ng-repeat="tab in mainController.tabs" heading="{{tab.title}}" select="mainController.setSelectedTab(tab.title)"></uib-tab>
</uib-tabset>
<div id="grid" ui-grid="mainController.gridOptions" class="grid"></div>
</div>
</body>
</html>
angularの基本的なところは省略します。
body部分を少し。
<input ng-model="mainController.filterValue" ng-change="mainController.update()" placeholder="名前検索"/>
この部分は単純な入力フィールドです。役割はGridのフィルタです。
MainController
クラスのfilterValue
と紐付けてあり、
入力がある度に同クラスのupdate()
を呼ぶようにしています。
update(): void {
this.gridApi.grid.refresh();
}
このupdate()
ではui-gridのrefresh()
をコールし、gridのリフレッシュをしています。
そしてrefresh()
がコールされるとgridOptions.onRegisterApi
で登録されている処理を実行します。
onRegisterApi: (gridApi) => {
this.gridApi = gridApi;
this.gridApi.grid.registerRowsProcessor((renderableRows:any) => {
var matcher = new RegExp(this.filterValue);
renderableRows.forEach(function(row: any) {
var match = false;
['name'].forEach(function(field){
if (row.entity[field].match(matcher)){
match = true;
}
});
if (!match){
row.visible = false;
}
});
return renderableRows;
}, 200 );
},
この部分がソレです。
読んで頂ければわかるのですが、gridの各行についてname
フィールドの値がfilterValue
の値にマッチしているかチェックし、
マッチしていればvisible = true
、マッチしなければvisible = false
にして
gridに表示させる行、させない行の設定を行っています。
次にタブですが
<uib-tabset>
<uib-tab ng-repeat="tab in mainController.tabs" heading="{{tab.title}}" select="mainController.setSelectedTab(tab.title)"></uib-tab>
</uib-tabset>
ここがUI Bootstrapを使ってタブ表示をしている部分です。
公式サンプルを見るに、本来であれば<uib-tab>~</uib-tab>
の中にgridを差し込むのですが
そうするとフィルタが上手く効かなかった為、今回はそうしませんでした。
タブが変更される度にMainController
クラスのchange(tab.title)
がコールされます。
change(tab: string): void {
this.selectedTab = tab;
if (this.selectedTab === "DATA1") {
this.gridOptions.data = this.getData(1);
}
else {
this.gridOptions.data = this.getData(2);
}
this.update();
}
change(tab: string)
メソッドでは
- this.selectedTabに現在選択されているTab値を格納 (特に必要ないが、とりあえず持っています)
- 現在選択されているタブ値に対応したデータを
gridOptions.data
に格納 -
update()
をコール(=gridのリフレッシュ)
という処理を行っています。
そして
<div id="grid" ui-grid="mainController.gridOptions" class="grid"></div>
この部分がui-gridを使用して、gridを表示している部分です。
MainController
クラスのプロパティであるgridOptions
で
gridの設定をしているのですがgridOptions.data
に設定されたデータが表示されます。
まとめると
- DataServiceクラスが今回のサンプルデータを管理している
- MainControllerがDataServiceを使い、grid表示をコントロールしている
- gridにはMainControllerクラスのgridOptions.dataに設定されているデータが表示される
- タブを切り替え時に
gridOptions.data
に設定されているデータを変更し、gridをリフレッシュすることでgridの表示データが変わる - filterフィールド(名前検索部)に文字が入力される度にgridをリフレッシュする。
- gridのリフレッシュ時、
gridOptions.onRegisterApi
に登録されている処理が実行され、
行ごとにフィルタ部に入力された文字列とname
フィールドをつき合わせ表示する行、しない行を設定後
gridのリフレッシュを行う
簡単ですが、以上です。