【SAP UI5】再レンダリング時のTreeTable展開状態の保持

## はじめに
&nbsp;本記事は、TreeTableを展開したまま、再レンダリングを行っても展開状態が保持される方法について記述しました。公式ドキュメントより、展開状態の保持について調査しましたが見つけることができなかったため、自力で実装することにしました。今回はその一例を紹介します。
結論だけ知りたい方は次のリンクから飛んでください（[解決方法](#解決方法)）

## 経緯
&nbsp;私はTreeTableのデータ更新時に再レンダリングをかけ、すぐに更新内容を表示しようと考えていました。
&nbsp;しかし、再レンダリングがかかるとTreeTableの展開ノードは全て閉じてしまう仕様であることに気づきました。
&nbsp;私は、ノード展開が閉じないようにすることはあきらめ、再レンダリング後に再び同じ状態に展開する方向性で進めることにしました。


## 過程
まず初めに、`treeTable.isExpanded()`を用いて、ノードが展開されている列番号を保持し、
それをもとに、再レンダリング後に展開状態の再現をしようと試みました。
下記がそのコードになります。
```js
this.expandList = [];
var tableSize = treeTable.getBinding().filterInfo.aFilteredContexts.length;
// 展開しているなら、そのノードのindexを取得
for (var i=0; i<tableSize; i++) {
    if (treeTable.isExpanded(i)) {
        this.expandList.push(i);
    }
}
```

```js
// 再レンダリング後に保持したindexをもとに展開
for (var row of this.expandList) {
    treeTable.expand(row);
}
```
しかし、この方法では、再レンダリング前の展開しているノードのindex情報を保存している為、
レンダリング時にデータの増減があった場合、以下の図1.のように列がずれてしまう問題が発生します。


図1.
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3345291/526d2050-4bb3-dd7d-2316-2fb610158f22.png)
`v`：展開しているノード
`>`：閉じているノード

したがって、index情報を保持するのではなく、展開しているノードそのもののデータを保持し、再レンダリング後のtreeTableから対象ノードのindexを取得することにしました。
ただし、単純にtreeTableから保持したノードと一致する、index番号を取得するだけでは、各ノードの展開状態による、index番号の変動を検出することができない為、以下のロジックを考えました。

##### 1. 展開しているノードの保持
##### 2. 全展開したtreeTableのデータをfor文でまわし、下記の条件でリストに格納する。
>・対象ノードと一致　➡　index番号（カウント変数）
>・対象ノードと不一致　かつ　hierarchyLevel != 1　➡　-1
>・親ノード(parentId)が展開状態である　➡　continue（格納せずスキップ）

##### 3. 格納データをforで回し、次の条件で最終的なindex番号を取得する。
>　・ 負の場合(-1)：変数に加算
>　・ 正の場合：index番号 - (-1を加算した変数)


例：下記の図2.の場合
保持した展開ノード：[1, 1.2, 2, 2.1]
2.の処理で格納したリスト：[0, -1, 3, -1, 8, 9]
3.の処理後のリスト：[0, 2, 6, 7]

図２．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3345291/e06cb253-a0a4-8709-da10-3066a3156351.png)

上記のように、最終的に取得したindex番号が[0, 2, 6, 7]になっており、図1で発生したようなずれがなく、正しいindex番号を取得できていることがわかります。
したがって、この方法であれば再レンダリング時にデータ数の増減があったとしても、正しくindex番号を取得し、再度同じ状態に展開することが可能です。




## 解決方法
今回の処理で実際に記述したコードを以下に記します。

##### 1. 展開しているノードの保持
```js
var treeTable = this.getView().byId(tableId);
var tableLength = treeTable.getBinding().filterInfo.aFilterContexts.length;

var targetNodeList = [];
for( var i = 0; i < tableLength; i++ ) {
    if( treeTable.isExpanded(i) ) {
        var targetRow = treeTable.getContextByIndex(i);
        var targetNode = targetrow.getProperty();
        targetNodeList.push(targetNode);
    }
}
```
##### 2. 全展開したtreeTableのデータをfor文でまわし、リストに格納する。
```js
var indexList = [];
var parentId = [];
var pushFlg = false;

// 正しい順序を取得するため全展開
treeTable.expandToLevel(tableLength);

for( var i = 0; i < tableLength; i++) {
    var targetRow = treeTable.getContextByIndex(i);
    if( targetRow != null) {
        var targetNode = targetRow.getProperty();

        for( node of targetNodeList) {
            //　対象ノードと一致した場合
            if(targetNode.objectId == node.objectId && targetNode.parentId == node.parentId){
                indexList.push(i);
                pushFlg = true;
                parentId.push(targetNode.objectId);
                break;
            }else {
                pushFlg = false;
            }
        }
    }
    // 親ノードが展開状態である場合
    if(parentId.includes(targetNode.parentId)) {
        continue;
    // 対象ノードと不一致　かつ　hierarchyLevel != 1 の場合
    }else if( !pushFlg && !(targetNode.hierarchyLevel == 1)) {
        indexList.push(-1);
    }
}

// 全展開したノードを全て閉じる
treeTable.collapseAll();


```
##### 3. 格納データをforで回し、最終的なindex番号を取得する。
```js
var space = 0;
var expandList = [];

for(var val of indexList) {
    // 閉じているノードの場合は「-1」を加算しindex番号調整
    if(val < 0) {
        space += val;
    // 展開ノードの場合、spaceを加算しindex番号取得
    } else {
        expandList.push(val+space);
    }
}

// 再レンダリング前の状態にノード展開
for(var row of expandList) {
    treeTable.expand(row);
} 

```

## まとめ
今回はtreeTableの展開状態の保持方法の一例を書いてみました。
調査を早々に切り上げて実装する方向性に切り替えたので、もっと簡単で良い方法があるかもしれませんので、一例として参考になれば幸いです。