Tabulator フィルター

 

 

 

 

 

ログインユーザーの登録レコードを表示するテーブルで、アドミンユーザーの場合、全グループのデータが表示されるので、Tabulatorのフィルターを使ってユーザー別にフィルターできるようにしました。

< コード例 >

アドミンユーザーの場合、マルチセレクトでグループを選べるセレクトタグを配置します。


// アドミンユーザーグループ名フィルター用 //
  if (isAdmin) {

    const uajax = new MyAjax("include/GetGroupNames.php", {})
    const groups = uajax.exec()
    
    // マルチセレクトタグ //
    const st = new SelectTag()
    const html = st.getGroups(groups, groups)
    //console.log(html)

    $(".groupsel").removeClass("dsp_none")
    $("#group_select").html(html)
    // マルチプルセレクト by vanillaSelectBox //
    let gpsel = new vanillaSelectBox("#groupsel", {
      maxWidth: 600,
      maxHeight: 600,
      minWidth: -1,
      //selected: cntrys + ""   <== これは出来ないので先にselectのoptionにセット
    })


  }

検索処理後、Tabulatorのフィルターを使って選んだグループ名で絞込みます。


    // アドミンユーザーフィルター値配列取得 //
    let filtervals = []
    if (isAdmin) {
      filtervals = $("#groupsel").val()
      //alert(filtervals)
    }


    // -C ブックリスト検索 (グループ名は指定しない) //
    const reqUserGroup = userGroup === "Admin" ? "%%" : userGroup
    const initajax = new MyAjax("include/GetBookList.php", {group: reqUserGroup, mindt: mindt, maxdt: maxdt})
    const seracheddata = initajax.exec()["data"]
    console.log(seracheddata)
    // グリッドの更新 //
    table.setData(seracheddata) // localなのでURLは要らぬ
    
    // アドミンユーザーフィルターの実行 //
    if (isAdmin) {
      table.setFilter("NM_GROUP", "in", filtervals);
    }
    // タイトルのレコード数  //
    $("#reccnt").text(table.getDataCount("active"))

< 画面例 >

初期設定 全グループ
グループ指定

Tabulator インライン編集と更新

先日まで、主にリードオンリーのデータを扱ってましたが、編集、更新が必要なデータを扱う必要があってドキュメントを見ながら、作ってみました。

< コード例・日付 >

カラムの設定で、editorとcellEditedを記述


 // 出発日 //
{
  title: "Dep Date",
  field: "DT_DEP",
  width: 90,
  hozAlign: "center", editor: "date", editorParams: {
    mask: "yyyy-MM-dd", // ????? 
    min: "2023-01-01", // the minimum allowed value for the date picker
    max: "2030-12-31", // the maximum allowed value for the date picker
    format: "yyyy-MM-dd", // the format of the date value stored in the cell
    elementAttributes: {
      title: "slide bar to choose option" // custom tooltip
    }
  },
  cellEdited: function (cell) {
    //cell - cell component
    
    // 追加の場合は何もしない //
    if (isAddingNow) {
      return false
    }

    const inpedData = cell.getData()
    const inpedDataJSON = JSON.stringify(cell.getData())

    // 行データ //
    console.log("--- inpedDataJSON (row)")
    console.log(inpedDataJSON)

    // データ列名 field //
    const fieldName = cell.getField()
    console.log("--- cell.getField()")
    console.log(fieldName)

    // cellの値 //
    const cellval = cell.getData()[fieldName]
    console.log("--- cellval")
    console.log(cellval)

    // テーブルのid //
    const tableId = cell.getData()["ID_BOOK"]


    //alert(inpedData + " に変わったよ\n" + inpedDataJSON)
    // サーバーに送信して保存する 他の列でも編集があるので、関数で行う //
    const updtres = postACellData(fieldName, cellval, tableId)
    if (updtres === "Success") {
      toastr.success("Date was updated")
    }
    else {
      toastr.error("Update error occured !!")
    }
  }
},

/**
   * セルフィールド値更新
   * @param {type} fieldname
   * @param {type} value
   * @param {type} id
   * @returns {.ajax@call;exec.result|res.result}
   */
  const postACellData = (fieldname, value, id) => {

    // -C セル値更新リクエスト
    const ajax = new MyAjax("update/UpdateABookField.php", {
      field: fieldname,
      value: value,
      id: id
    })
    const res = ajax.exec()
    return res["result"]

  }

サーバー側

<?php

/**
 * aviation BookedList セル値更新
 * UpdateABookField.php
 *
 * 2023-01-06 tanulator aviation用
 * 
 */

header("Access-Control-Allow-Origin:*");
header("Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept");
header("Content-type: application/json");

ini_set('default_charset', 'UTF-8');
ini_set('display_errors', 0);
//ini_set("memory_limit", "200M");
ini_set("memory_limit", "-1");

date_default_timezone_set('Asia/Tokyo');

/* * **************************************************** */

$SQL = <<<EOM
UPDATE h_book
SET ##FIELD## = :VALUE
WHERE ID = :ID
EOM;

// == リクエストパラメータ == //
$field = $_POST["field"] ? $_POST["field"] : $_GET["field"];
$value = $_POST["value"] ? $_POST["value"] : $_GET["value"];
$id = $_POST["id"] ? $_POST["id"] : $_GET["id"];

if (!$field) {
    $field = "DT_DEP"; 
}
if (!$value) {
    $value = "2023-01-01"; 
}
if (!$id) {
    $id = 296; 
}

/*echo $field."\n";
echo $value."\n";
echo $id."\n";*/

$res = array();
try {

    // 接続定義 //
    $connect_db = "mysql:dbname=aviation;host=localhost;charset=utf8;";
    $connect_user = '????';
    $connect_passwd = '????????';

    //データベース接続 //
    $dbh = new PDO(
        $connect_db,
        $connect_user,
        $connect_passwd
    );

    $sql = str_replace("##FIELD##", $field, $SQL);
    //echo $sql;

    // パラメータセット //
    $stmt = $dbh->prepare($sql);
    //$stmt->bindvalue(':FIELD', $field, PDO::PARAM_STR);
    $stmt->bindvalue(':VALUE', $value, PDO::PARAM_STR);
    $stmt->bindvalue(':ID', $id, PDO::PARAM_INT);
    $stmt->execute();

    $res["result"] = "Success";

} catch (PDOException $e) {
    //var_dump($e->getMessage());
    $res["result"] = "Fail";
}

// 切断
$dbh = null;

echo json_encode($res);

?>

< 画面例 >

Tabulator でツールチップ

 

 

 

Tabulatorのツールチップ表示につきましては、最初よくドキュメントを読まないで試した時、エラーになったりして表示できなく、それがプチとらうまとなって、DOMエレメント操作でtippy.jsのそれを使ってました。
ところ、今度はtippyが動作しなくなったので、Tabulatorのドキュメントを読み直して試したところできましたので備忘録しておきます。

< コード例 >


// オペレータロゴ //
{
    title: "|#",
    field: "opeflag",
    width: 26,
    hozAlign: "center",
    formatter: function (cell, formatterParams, onRendered) {

        const ope = cell.getRow().getData().NO_FLIGHT.slice(0, 2).toLowerCase()
        //console .log(ope)
        
        // 非可視にした他の列の値を参照 //
        const opename = cell.getRow().getData().NM_AIRLINE
        const icao = cell.getRow().getData().CD_ICAO
        const callsign = cell.getRow().getData().NM_CALLSIGN
        
        // 他の列の値を画像の親スパンの属性にセットする //
        const img = `<span iata="${ope.toUpperCase()}"
        opename="${opename}" icao="${icao}" callsign="${callsign}">
        <img src="Img/airline/airline_${ope}_square.png" width="20"
        onerror="this.style.display='none'"/>
        </span>`
        return img
    },
    tooltip: true,      // trueにしてツールチップを表示可能にする
    
    // ツールチップが見える時 //
    tooltip: function (e, cell, onRendered) {
        //e - mouseover event
        //cell - cell component
        //onRendered - onRendered callback registration function
        
        // エレメントを生成 //
        var el = document.createElement("div")
        el.style.backgroundColor = "black"
        el.style.color = "white"
        
        // formatterでセットした属性 //
        const cellelm = cell.getElement()
        const opename = $(cellelm).children("span").attr("opename")
        const icao = $(cellelm).children("span").attr("icao")
        const callsign = $(cellelm).children("span").attr("callsign")
        
        // HTMLにしてリターン //
        $(el).html(`${icao} : ${callsign}<br>${opename}`)

        return el
    },

},

< 画面例 >

フライト番号からIATAがわかるので、ICAOとコールサインとエアライン名をポップアップ

 

 

 

 

 

FlightradarのAPiでとれたカレントステータスをポップアップ

Tabulator 日本語化

 

 

 

ここで教えていただきました。

< コード例 >


      locale: true,   //trueならばブラウザの言語設定を自動検出 'ja'のように指定することも可能
      langs: {
        ja: {
          //データ読込関連のテキストの翻訳
          data: {
            loading: '読込中',
            error: 'エラー',
          },
          //ページネーション関連のテキストの翻訳
          pagination: {
            page_size: '表示件数',
            page_title: 'ページを表示',
            first: '最初',
            first_title: '最初のページ',
            last: '最後',
            last_title: '最後のページ',
            prev: '前',
            prev_title: '前のページ',
            next: '次',
            next_title: '次のページ',
            all: 'すべて',
            counter: {
              showing: '表示中',
              of: '/',
              rows: '件',
              pages: 'ページ',
            },
          },
        },

      },

< 画面例 >

[商品価格に関しましては、リンクが作成された時点と現時点で情報が変更されている場合がございます。]

JavaScriptコードレシピ集 スグに使えるテクニック278 最新ECMAScri [ 池田泰延 ]
価格:3278円(税込、送料無料) (2023/1/20時点)

楽天で購入

 

 

Tabulator のチャートを使ってみる

columns の formatter を progress にします。

詳しくはこちら

< コード例 >

データはサーバー側で用意してます。


  // パーセンテージプログレスバー //
  {
    title: "TTS Ratio", field: "PCNT_TTS", formatter: "progress", formatterParams: {
      min: 0,                   // 範囲最小値
      max: 100,                 // 範囲最大値
      color: ["#008600"],       // 棒グラフ色
      legend: true,             // 値表示 or not
      legendColor: "#FFFFFF",   // 値色
      legendAlign: "justify",   // 値位置
    },
    width: 100,
    topCalc: "avg", bottomCalcParams: { precision: 1 },
    /*formatter: function (cell, formatterParams, onRendered) {
      return Math.round(cell.getValue() / 5)
    },*/
  }

< 画面例 >

過去1年分の為替レートの最大を100, 最小を0とした率を表示

Tabulator rowFormatterを使って、文字色を変化させる

 

 

 

Tabulator でカラムのformatter設定で文字色を変えたり、画像を表示したりできますが、列が多くなると見通しが悪くなるので、rowFormatterで同様の処理をしてみました。

< コード例 >


// @@ 行をフォーマットする時 @@ //
      rowFormatter: (row) => {

        // ++ 各行の列走査をしてエレメントを取得 ++ //
        $.each($(row.getElement()).children(''), function (j, itm) {

          
          // 属性に 'tabulator-field'がある場合 //
          const fld = $(this).attr('tabulator-field');

          // TTS前営業日差 //
          if (fld === 'RT_TTS_LASTDIFF') {
            const currDiff = row.getData().RT_TTS_LASTDIFF;
            if (currDiff >= 0) {
              $(this).text('+' + numeral(currDiff).format('#,###.#0'));
              $(this).css('color', 'blue');
            } else {
              $(this).css('color', 'red');
            }
          }

          // TTB前営業日差 //
          if (fld === 'RT_TTB_LASTDIFF') {

            const currDiff = row.getData().RT_TTB_LASTDIFF;
            const currTTB = row.getData().RT_TTB;

            if (currTTB <= 0) {
              $(this).text("")
            }
            else {

              if (currDiff >= 0) {
                $(this).text('+' + numeral(currDiff).format('#,###.#0'));
                $(this).css('color', 'blue');
              } else {
                $(this).css('color', 'red');
              }

            }
          }

          // セルの右罫線色変更 //
          if (fld === 'CD_CURR' && $(this).text().length === 3) {
            let curstyleV = $(this).attr('style');
            curstyleV += 'border-right-color:#3c3c3c;';
            $(this).attr('style', curstyleV);
          }

          // ドラッグドロップ行移動説明 //
          if ($(this).hasClass("tabulator-row-handle")) {
            $(this).attr('title', "ドラッグドロップしてこの行を移動出来ます");
            $(this).addClass('tippydiv');
          }

        });
      },

< 画面例 >

銀行発表の日次為替レート
前営業日より上がった場合、青文字、下がった場合赤文字。

[商品価格に関しましては、リンクが作成された時点と現時点で情報が変更されている場合がございます。]

かんたんJavaScript ECMAScript2015対応版 (プログラミングの教科書) [ 高橋広樹 ]
価格:2838円(税込、送料無料) (2023/1/19時点)

楽天で購入

 

 

[商品価格に関しましては、リンクが作成された時点と現時点で情報が変更されている場合がございます。]

JavaScript逆引きレシピ 第2版 [ 山田 祥寛 ]
価格:3,080円(税込、送料無料) (2023/5/23時点)

楽天で購入

 

 

Tabulator で 行クリックを動的発生させてメイン表と明細表を同時に表示させる

 

 

 

マスタリンクしている2表があってメインレコードをクリックして明細表を横やサブグリッドで表示することはよくあると思います。
メイン表を読み込んだ時点で明細表も同時に表示したくなり、
これもStackOverFlowで同様のことをしたい人を見つけ、回答を参考にして書いてみました。

< コード例 >

カラムには”id”の列が必要なので設定
“id”はサーバー側でセット済


 { field: "id", visible: false }

tableBuiltのイベントでメイン表の1行目をクリックさせる


  // @@ 機材別合計テーブル生成時 @@ //
    table.on("tableBuilt", function () {
  
      // == 省略 == //

      // == 1行目クリック == //
      const fstrow = table.getRow(1)     // <=== 合計計算行を上に配置したのでインデックスは1になる(と思う)
      const fstrowelm = fstrow.getElement()
      $(fstrowelm).trigger('click')

    })

メインテーブルの行クリックイベント


    // @@ メインテーブル行をクリックした時 @@ //
    table.on("rowClick", function (e, row) {
      //e - the click event object
      //row - row component

      // == 省略 == //

      // -C 明細データリクエスト取得 //
      const dlg = new OpeEqDialog()
      let details = dlg.getModelDetails(al_icao, name)["rows"]
      details.forEach((tmp, i) => {
        if (OpeEqDialog.active_regnumbers.includes(tmp.nm_regist)) {
          tmp.is_active = "*"
        } else {
          tmp.is_active = ""
        }

        tmp["id"] = i
      })

      console.log(details)

      // 番号コード明細テーブル表示 //
      dlg.showDetailTable(details, name)

    })

< 画面例 >

1行目クリックを発生させない場合
明細テーブルが非表示

1行目クリックを発生させる
明細テーブルが表示される、更に明細テーブルにも同様の処理を追加して、機材の写真と登録履歴を横に表示

Tabulator で行に下線をつける

 

 

 

Tabulator でグループピングするまでもないものの、区切り線で分ければわかりやすくなるので、ソートされたカラムの値でグループで別れてる変わり目に区切り線をつけてみました。

< コード例 >

tableBuiltのリスナーで行を読み込んで下線を引く行にクラスを追加しています。

CSS


.border-bottom-blue1 {
    border-bottom: solid 1px blue
}

JavaScript


        // @@ tabulator テーブル生成完了時 @@ //
        table.on('tableBuilt', function () {

            console.log('--- data loaded ---')
            console.log(new Date().getTime())

            // テーブルの行を配列にする //
            const rows = table.getRows()

            // 日付変化下線用日付配列 //
            let dates = []
            rows.forEach((row) => {
                dates.push(row.getData().subgDepDate)
            })
            console.log(dates)

            // 日が変わる行のインデックス位置を配列にする //
            let underlineIdxs = []
            dates.forEach((dt, i) => {
                if (i > 0 && dt !== dates[i - 1]) {
                    underlineIdxs.push(i - 1)
                }
            })
            console.log(underlineIdxs)

            // ++ 行を走査して下線位置に下線を引く ++ //
            rows.forEach((row, i) => {
                // 該当行の場合 //
                if (underlineIdxs.includes(i)) {
                    const elm = row.getElement()
                    // 下線クラスを追加 //
                    $(elm).addClass("border-bottom-blue1")
                }
            })

        })

< 画面例 >
スカイマークの飛行機の日付別運航履歴がわかりやすくなりました。

 

Tabulator でクリップボードにコピー

 

 

 

Tabulator に copyToClipboard メソッドがあり、特にDOMを操作しなくても出来ました。
方法は公式ドキュメントに詳しく書かれています。

< コード例 >

プロパティ


  // ++ Tabulator ++ //
  let table = new Tabulator("#altable", {


    // クリップボード設定 //
    clipboard: true,
    clipboardCopyConfig: {
      columnHeaders: true, //do not include column headers in clipboard output
      columnGroups: false, //do not include column groups in column headers for printed table
      rowGroups: false, //do not include row groups in clipboard output
      columnCalcs: true, //do not include column calculation rows in clipboard output
      dataTree: false, //do not include data tree in printed table
      formatCells: false, //show raw cell values without formatter
    },

    // == 他は省略 == //

  })

テーブル生成後のイベントでステータスバーにコピー用アイコンを配置して、クリックのリスナーを書いてます。


  // @@ テーブル生成時 @@ //
  table.on("tableBuilt", function () {

    // コピーボタン挿入 //
    const html = `<span id="clipbtn" class="ml-2 mr-2 curpo tippydiv"
    title="Copy to clipboard all records">
    <img src="Img/copy_64_darkgray.png" width="20"/>
    </span>`
    const ftcnts = $(".tabulator-footer-contents").eq(0)
    $(ftcnts).prepend(html)

    // @@ コピーボタンを押した時 @@ //
    $("#clipbtn").on("click", function (e) {
      table.copyToClipboard("all") //copy the currently selected rows to the clipboard
      toastr.info("All records were copied to clipboard")
    })

    // tippy //
    tippy('.tippydiv', {
      placement: 'top',
      animation: 'scale',
      duration: 1000,
      arrow: true
    })

  })

< 画面例 >

Tabulator で クリックした行の文字色を変えて強調

 

 

 

Tabulator でクリックした行の明細表を別テーブルで表示する時、メインテーブルのレコードが多い場合、どの行をクリックしたのかがわからなくなるのを防ぐ為にクリックした行の文字色を変えて強調させてみました。

< コード例 >

まずはテーブルが生成された時の行文字色を取得してクラス変数にセット


    // @@ 明細テーブルが生成された時 @@ //
    table.on("tableBuilt", function () {

      const componentToHex = (c) => {
        var hex = c.toString(16)
        return hex.length == 1 ? "0" + hex : hex
      }

      const rgb2hex = (r, g, b) => {
        console.log(r, g, b)
        return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b)
      }
 
      // セル文字色取得 //
      const cell = table.getRow(1).getCell("code").getElement()
      const style = window.getComputedStyle(cell)
      const rgbcol = style.getPropertyValue('color')
 
      const rgbtxt = rgbcol.replace("rgb(", "").replace(")", "").replaceAll(" ", "")
   
      const rgbary = rgbtxt.split(",")
      this.cellColorDetail = rgb2hex(rgbary[0], rgbary[1], rgbary[2],)


    })

セルをクリックした時


 // @@ テーブル行をクリックした時 @@ //
    table.on("rowClick", function (e, row) {
      //e - the click event object
      //row - row component

      // 全行文字色をテーブル生成時の文字色にして初期化 //
      const rows = table.getRows()
      rows.forEach(tmp => { tmp.getElement().style.color = this.cellColor })
      // 強調 //
      row.getElement().style.color = "#0000FF"

      // == 以下省略 == //

    })

    return table
  }

< 画面例 >