Sencha Ext JS 6 のツリーリスト その1

Sencha Advent Calendar 2015 22日目
http://www.adventar.org/calendars/1041

## ツリーリストを触ってみる

こんにちは。xenophyの菅井です。
お久しぶりです。今回のアドベントカレンダーも終盤に差し掛かってきました。
自分の番が終わればあと２回です。
アドベントカレンダーが終わったら、もう今年も終わりますねー。なんだか早かった。  

さて今日から明日にかけてExt JS 6から追加されたコンポーネントで最近個人的に使った
ツリーリスト(`Ext.list.Tree`)をコード触りつつ簡単に使い方を紹介していこうと思います。

## ツリーリストについて

ExtJSのツリーといえば元々ツリーパネルがありました。
あちらはグリッドパネルみたいにツリーの横にカラムを追加できたり
リーフにチェックボックスを付けたりツリー表示に色々拡張ができました。

今回紹介するツリーリストはほんとにツリー状のリストというだけで
すごくシンプルな作りなのでナビゲーションの機能を実装するのに向いていると考えています。

さらにツリーリストにはツリーパネルにはない機能があって
「micro」モードと呼ばれているものがあります。

この「micro」モードになるとツリーリストはアイコンのみのリスト表示になって
その分リストの幅を狭く表示できるのでナビゲーションの縮小機能を
とても簡単に実装することができます。

というわけで今日はこのツリーリストを使ってナビゲーションのベースを作成していきます。
今日の目標はツリーパネルの表示と「micro」モード切り替え部分の実装です。

## まずは

Ext JS 6のアプリケーションを新規で作成します。

> % `sencha -sdk` ~/Library/Sencha/ext-6.0.1/ `generate app` ExtJs6TreeList extjs-6-treelist

* Sencha CmdやSDKの詳細についてはここでは説明していません。別の日の記事が参考になると思います！

Sencha Cmdでローカルのサーバーを起動

> % `sencha web start`  

ExtJS6の新規アプリケーションの画面が表示されるのを確認します。

## 間仕切り

はじめに新規で作成したアプリケーションの画面にヘッダー、サイドバー、スクリーンの枠組みを
それぞれ作っていきます。

* 今回ナビゲーションを表示するのはサイドバーの部分です。

作成されたディレクトリをたどると`~/extjs-6-treelist/classic/src/view/main/Main.js`というファイルがあるので
これをベースに改造していきます。

* 今回modernは省きます。ツリーリスト自体はmodernも対応しています。　  

この`Main`クラスですが`Ext.tab.Panel`を継承しているので普通のパネルにします。
さらにそれぞれのレイアウトはボーダーで切っていきます。
さらにさらに色々いらないconfig等を外して下記のようになりました。

#### ソース

    Ext.define('ExtJs6TreeList.view.main.Main', {
        extend: 'Ext.panel.Panel',
        xtype: 'app-main',
    
        requires: [
            'Ext.plugin.Viewport',
            'Ext.window.MessageBox',
    
            'ExtJs6TreeList.view.main.MainController',
            'ExtJs6TreeList.view.main.MainModel'
        ],
    
        controller: 'main',
        viewModel: 'main',
    
        // ボーダーレイアウト
        layout: 'border',
    
        // パネル全体に枠線を出す
        defaults: {
            frame: true
        },
    
        items: [{
            // ヘッダー部分のパネル
            xtype   : 'panel',
            region  : 'north',
            height  : 50,
            name    : 'header',
            html    : 'ヘッダー'
        }, {
            // サイドバー部分のパネル
            xtype   : 'panel',
            region  : 'west',
            width   : 300,
            name    : 'sidebar',
            html    : 'サイドバー'
        }, {
            // スクリーン部分のパネル
            xtype   : 'panel',
            region  : 'center',
            name    : 'screen',
            html    : 'スクリーン'
        }]
    });

#### 画面

![border](https://goo.gl/Ael5tv)

## ツリーリストを表示

やっと主役の登場です。
今作成したサイドバーパネルにツリーリストを表示するため
ツリーリストを継承したViewを作成します。

#### ツリーストアの作成

まずツリーに表示させるデータを持ったツリーストアを下記のように作成します。
階層のデータになっているのはなんとなく解ると思います。
`iconCls : 'x-fa fa-dashboard'`でリストの横に表示するアイコンを指定しています。

* この指定はツリーパネルも同様です。

ファイル名：`~/extjs-6-treelist/app/store/Navigation.js`  
ソース：

    Ext.define('ExtJs6TreeList.store.Navigation', {
        extend: 'Ext.data.TreeStore',
    
        alias: 'store.navigation',
    
        root: {
            expanded    : true,
            children    : [{
                text    : 'ダッシュボード',
                iconCls : 'x-fa fa-dashboard',
                leaf    : true
            }, {
                text    : '日報',
                iconCls : 'x-fa fa-edit',
                leaf    : true
            }, {
                text        : '管理',
                iconCls     : 'x-fa fa-gears',
                expanded    : true,
                children    : [{
                    text    : 'アカウント',
                    iconCls : 'x-fa fa-users',
                    leaf    : true
                }, {
                    text    : 'プロフィール',
                    iconCls : 'x-fa fa-user',
                    leaf    : true
                }, {
                    text    : '権限',
                    iconCls : 'x-fa fa-bolt',
                    leaf    : true
                }]
            }]
        }
    });

#### ツリーリストの作成

ツリーリストはMVVMの構成で作ってみます。
中身はすごい簡単で、単純に`Ext.list.Tree`を継承して
データソースに上記のストアを指定しているだけです。
`xtype`は「navigation」とします。

###### View

ファイル名：`~/extjs-6-treelist/classic/src/view/navigation/Main.js`  
ソース：

    Ext.define('ExtJs6TreeList.view.navigation.Main', {
        extend: 'Ext.list.Tree',
        xtype: 'navigation',
    
        requires: [
            'ExtJs6TreeList.view.navigation.MainController',
            'ExtJs6TreeList.view.navigation.MainModel',
    
            'ExtJs6TreeList.store.Navigation'
        ],
    
        store: {
            type: 'navigation'
        }
    });


###### ViewModel

ファイル名：`~/extjs-6-treelist/app/view/navigation/MainModel.js`  
ソース：

    Ext.define('ExtJs6TreeList.view.navigation.MainModel', {
        extend: 'Ext.app.ViewModel',
    
        alias: 'viewmodel.navigation'
    });

###### ViewController

ファイル名：`~/extjs-6-treelist/app/view/navigation/MainController.js`  
ソース：

    Ext.define('ExtJs6TreeList.view.navigation.MainController', {
        extend: 'Ext.app.ViewController',
    
        alias: 'controller.navigation'
    });

#### Mainの修正

`~/extjs-6-treelist/classic/src/view/main/Main.js`のファイルを修正します。
まず`requires`の配列の中に作成したツリーリストのクラス`ExtJs6TreeList.view.navigation.Main`を追加します。
次にサイドバーの`items`の中にツリーリストの`xtype`を指定します。そのとき`html:'サイドバー'`は削除します。

    {
        // サイドバー部分のパネル
        xtype   : 'panel',
        region  : 'west',
        width   : 300,
        name    : 'sidebar',
        items   : [{
            xtype: 'navigation'
        }]
    }

#### リフレッシュ

Mainにツリーリストのクラスを追加したので`sencha app refresh`でクラスの紐付けを更新します。  
> % `sencha app refresh`  

#### 画面

上記の修正後画面を表示するとツリーリストが表示されています。

![treelist](https://goo.gl/bfChtm)

## ツリーリストの「micro」モード

ここまでできれば今回の目標は達成です。あともう少し。

#### 「micro」モード用のチェックボックスを配置

ツリーリストを「micro」モードにフォームチェンジするためのチェックボックスを配置します。
ヘッダーに置くのでMainのヘッダーパネルを下記のように修正します。  

    {
        // ヘッダー部分のパネル
        xtype   : 'panel',
        region  : 'north',
        height  : 50,
        name    : 'header',
        items   : [{
        	// 「micro」モードにフォームチェンジするためのチェックボックス
        	xtype       : 'checkboxfield',
        	boxLabel    : 'change micro mode',
        	name        : 'changeMicroMode',
        	handler     : 'onChangeMicroMode'
       	}]
    }

次にチェックボックスをチェックしたときに「micro」モードにしたいのでMainのViewControllerの中で
下記のイベントハンドラーを実装します。
メソッドの名前はViewの`handler`に指定してある`onChangeMicroMode`です。
このメソッドの中でツリーリストのViewを取得して`setMicro`のメソッドを利用し
「micro」モードと通常モードの切り替えを行っています。
その際モード毎にサイドバーパネルの幅を切り替えています。

    Ext.define('ExtJs6TreeList.view.main.MainController', {
        extend: 'Ext.app.ViewController',

        alias: 'controller.main',

        onChangeMicroMode: function(cb, checked) {

            var me          = this,
                view        = me.getView(),
                navigation  = view.down('treelist[xtype=navigation]'),
                sidebar     = view.down('panel[name=sidebar]');

            // 「micro」モード切り替え
            navigation.setMicro(checked);
            // サイドバーパネルの幅切り替え
            sidebar.setWidth(checked ? 36 : 300);
        }
    });

ここまできたら最後に`sencha app build`をして画面を表示したいのですが
今のままだとビルドエラーになってしまいます。

* そしておそらくビルドしないとチェックボックスのスタイルが当たらない

ビルドエラーになる理由は元々タブパネルだったMainのViewを普通のパネルに変えているので
元々新規作成のときにつくられたタブパネル向けのsassの記述が残ったままになっているためです。
なので`~/extjs-6-treelist/classic/sass/src/view/main/Main.scss`の中身を削除してビルドします。
> % `sencha app build`  

#### 表示確認

最後に画面表示して「micro」モードにしてみる。
「micro」モード表示できました！サイドバーパネルも「micro」モードに合わせて狭くなっています。

![micromode](https://goo.gl/tP18PY)

## 次回予告

目標のツリーリストの表示と「micro」モードの切り替えまでできました。
ただ今のままだと階層に関係なくフラットに表示されていたり
通常モードと「micro」モードのアイコンの位置が違うので
切り替えていると動きが不自然に見えたります。
明日はsassメインでこの辺の見た目のカスタマイズをしてみます。