Tabulator formatterでフォーマットしたセルのhtmlをタイマーで更新する

 

 

 

Tabulatorでタイマーで動的に更新された値をもとにしたHTMLでセルの表示を部分的に変更させてみました。
formatterを使ってない場合は、JSONの配列を使って簡潔に出来そうですが、HTMLの部分入替がややこしくなるので、jQueryでDOMエレメントを書き換えています。

< コード例 >

Unix時刻フォーマット関数


/**
   * Unix時刻(ms)指定 時:分 フォーマット時刻
   * @param {type} utm ミリ秒Unix時刻
   * @param {type} incdate 日付有無
   * @param {type} withS 秒有無
   * @returns {String}
   */
  getFormatLocalTime2(utm, incdate, withS = false, withYMD = false) {

    //console.log(utm);

    const lTM = new Date(utm);
    const currMS = lTM.getTime();
    const Y = lTM.getFullYear();
    const M = lTM.getMonth() + 1;
    const D = lTM.getDate();
    let h = lTM.getHours();
    let n = lTM.getMinutes();
    let s = lTM.getSeconds();

    h = h.toString().length === 1 ? "0" + h : h;
    n = n.toString().length === 1 ? "0" + n : n;
    s = s.toString().length === 1 ? "0" + s : s;

    const tmtxt = h + ":" + n;
    const tmtxtWithDate = Y + "-" + (M < 10 ? "0" : "") + M + "-" +
            (D < 10 ? "0" : "") + D + " " + h + ":" + n;

    let res = ""
    if (incdate) {
      res = tmtxtWithDate;
    }
    else {
      res = tmtxt;
    }

    if (withS) {
      res += ":" + s
    }
    
    if (withYMD) {
      res = Y + "-" + M + "-" + D + " " + res
    }

    return res

  }

現在時刻タイマー


  // 時計スタート時刻 //
  const sttmutc = Number(sttm - UTC_OFFSET_SECONDS * 1000)

  const lt = new LocalTimes()

  let tmtxt = lt.getFormatLocalTime2(sttmutc, 0, true)
  $("#utctime").text(tmtxt + " (UTC)")
  // tableのcellのformatterで使う //
  let tmtxtUTCLong = lt.getFormatLocalTime2(sttmutc, 0, true, true)
  //console.log(tmtxtUTCLong)


  tmtxt = lt.getFormatLocalTime2(sttm, 0, true)
  $("#yourtime").text(tmtxt + " (You)")

  // == 時計時刻タイマー == //

  let tmpos = 0
  const clockTimer = setInterval(() => {

    const curtmutc = sttmutc + parseInt(1000 * tmpos)
    let tmfmt = lt.getFormatLocalTime2(curtmutc, 0, true)
    //console.log(tmfmt)
    tmtxtUTCLong = lt.getFormatLocalTime2(curtmutc, 0, true, true)
    //console.log(tmtxtUTCLong)

    $("#utctime").text(tmfmt + " (UTC)")

    const curtmyou = sttm + parseInt(1000 * tmpos)
    tmfmt = lt.getFormatLocalTime2(curtmyou, 0, true)
    $("#yourtime").text(tmfmt + " (You)")

    if (tmpos % 30 === 0 && tmpos !== 0) {
      // セルのAgo minutesを書き換え //
      updateRowDiffTimes(lt, tmtxtUTCLong)
    }


    tmpos++;


  }, 1000)   // <=== 1秒間隔

カラムの初期設定


        // ログ追加 クライアント時刻 UTC時刻 更新遅延秒 //
        {
          //headerTooltip: true,
          title: "Your Time", field: "TM_ADD_UTC_SHORT",
          width: 50, hozAlign: "center",
          formatter: function (cell, formatterParams, onRendered) {

            // 反転させて強調 //
            const cellelm = cell.getElement()
            $(cellelm).on("mouseover", function (e) {
              $(this).addClass("text_reverse")

            })
            $(cellelm).on("mouseout", function (e) {
              $(this).removeClass("text_reverse")
            })

            /**
             * Now - UTC追加時刻 分取得
             */
            const diffMinutesToNow = (datatm, nowtm) => {
              const lts = new LocalTimes()
              return Math.round(lts.getMinuteTimeDifference(datatm, nowtm))
            }

            const utcfmt = cell.getData().TM_ADD_UTC
            const dt = utcfmt.split(" ")[0]
            const tm = utcfmt.split(" ")[1]
            const y = dt.split("-")[0]
            let m = dt.split("-")[1]
            let d = dt.split("-")[2]
            let h = tm.split(":")[0]
            let n = tm.split(":")[1]
            let s = tm.split(":")[2]
            m = m.slice(0, 1) === "0" ? m.slice(1) : m
            d = d.slice(0, 1) === "0" ? d.slice(1) : d
            h = h.slice(0, 1) === "0" ? h.slice(1) : h
            n = n.slice(0, 1) === "0" ? n.slice(1) : n
            s = s.slice(0, 1) === "0" ? s.slice(1) : s
            const dttm = new Date(y, m, d, h, n, s)
            const dttmvalUTC = dttm.getTime()
            const dttmvalLocal = Number(dttmvalUTC) + Number(UTC_OFFSET_SECONDS * 1000)


            const lt = new LocalTimes()
            const ltfmt = lt.getFormatLocalTime2(dttmvalLocal, 0, false, false)

            const secagotxt = "SA " + cell.getData().UPDATED_SEC_AGO

            const diffToNow = diffMinutesToNow(cell.getData().TM_ADD_UTC, tmtxtUTCLong)

            return  ltfmt + "<br>" + cell.getValue() + "<br>" + secagotxt + "<br>" +
                    diffToNow + "MA"

            // tmtxtUTCLong

          },

タイマーで更新した値にしてHTMLを部分的書き換え


  /**
   * テーブルの差異分更新
   * @param {type} lt 時刻処理ユーティリティクラスのインスタンス
   * @param {type} currtmtxt 現在時刻テキスト
   * @returns {undefined}
   */
  const updateRowDiffTimes = (lt, currtmtxt) => {

    const rows = table.getRows()
    //console.log("rows.length:" + rows.length)

    // ++ テーブル行走査 ++ //
    rows.forEach((row) => {

      const utclong = row.getData().TM_ADD_UTC

      // ++ jQueryによる列走査 ++ //
      $.each($(row.getElement()).children(""), function (j, itm) {

        const fld = $(this).attr("tabulator-field")

        // UTC時刻列の差異分を書き換え //
        if (fld === "TM_ADD_UTC_SHORT") {

          //console.log($(this).html())
          let html = $(this).html()
          let ary = html.split("<br>")

          // セルデータのUTC時刻と今との差異分 //
          const diffToNow = Math.round(lt.getMinuteTimeDifference(utclong, currtmtxt)) + "MA"
          //console.log(diffToNow)

          // セルを書き換え //
          ary[3] = diffToNow
          $(this).html(`${ary[0]}<br>${ary[1]}<br>${ary[2]}<br>${ary[3]}`)

        }

      })
    })


  }

< 画面例 >

タイマーで30秒に1回、離着陸が何分前であるかを更新していく

Tabulator 行コンテキストメニュー ビルトインファンクション編

ドキュメントはこちら

 

 

< コード例 >


    rowContextMenu: function (component, row) {
      //component - column/cell/row component that triggered the menu
      //e - click event object  <=== eventではなくrow

      console.log("--- component")
      console.log(component)

      console.log("--- e")
      console.log(row.getData().iata_code)
      //console.log(e.Target._row.data)


      const icao = row.getData().icao_code
      const iata = row.getData().iata_code
      const name = row.getData().name

      const icon = `Img/airline/airline_${iata.toLowerCase()}_square.png`


      let menu = [];

      let itm = {
        label: "<span class='mr-1'>" + "<img src='" + icon + "' width='20'/></span>" + icao + " Show LiveMap",
        action: function (e, column) {


          // -C モーダルダイアログで表示 //
          const lm = new LiveMap("#modal_livemap", "livemap-container")
          lm.setAirportLatLon(-500, -500)
          lm.setUserGroup(userGroup)
          lm.setUserId(userId)
          lm.showDialog(icao, iata, name)

        }
      }
      menu.push(itm)

      return menu;
    }

< 画面例 >

Tabulator カスタムページネーターの配置

 

 

 

テーブルが縦に長い場合、上の方にも配置したページネーターでページ移動出来ると便利なので配置してみました。

< コード例 >

HTML


<!-- ページネーション -->
<div id="top_pagenator" class="ml-4 mt-0 pt-0">
  <span id="topp_first" class="mr-3 curpo topps tippyspan" title="Move to first page"><i class="fa fa-angle-double-left"></i></span>
  <span id="topp_prev" class="mr-3 curpo topps tippyspan" title="Move to previous page"><i class="fa fa-angle-left"></i></span>
  <span id="topp_next" class="mr-3 curpo topps tippyspan" title="Move to next page"><i class="fa fa-angle-right"></i></span>
  <span id="topp_last" class="mr-3 curpo topps tippyspan" title="Move to last page"><i class="fa fa-angle-double-right"></i></span>
  <!-- 9/99 -->
  <span id="topp_position" clss="text-white"></span>
</div>

Javascript
paginationCounterの関数


    paginationCounter: function (pageSize, currentRow, currentPage, totalRows, totalPages) {

      $("#topp_position").text(`${currentPage}/${totalPages}`)
      return "Showing " + pageSize + " rows of " + totalRows + " total";
    },

アイコンクリックのリスナー


  $(".topps").off("click")

  // @@ トップページネーションアイコンをクリックした時 @@ //
  $(".topps").on("click", function (e) {

    let firstButton
    let prevButton
    let nextButton
    let lastButton

    // ページネーションボタンをセット //
    $.each($("button"), function (j, btn) {

      // tabulator-pageクラス内でテキストが合致する場合、セットする //
      if ($(this).hasClass("tabulator-page")) {
        const btntxt = $(this).text()
        if (btntxt === "First") {
          firstButton = $(this)
        }
        else if (btntxt === "Prev") {
          prevButton = $(this)
        }
        else if (btntxt === "Next") {
          nextButton = $(this)
        }
        else if (btntxt === "Last") {
          lastButton = $(this)
        }
      }

    })

    const tp = $(this).attr("id").split("_")[1]

    // ボタンクリックを発生させる //
    switch (tp) {
      case "first":
        $(firstButton).trigger("click")
        //pgPos = 1
        break
      case "prev":
        $(prevButton).trigger("click")
        //pgPos--
        break
      case "next":
        $(nextButton).trigger("click")
        //pgPos++
        break
      case "last":
        $(lastButton).trigger("click")
        //pgPos = pageSize
        break
    }


  })

< 画面例 >

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

航空ファン 2023年2月号【雑誌】【1000円以上送料無料】
価格:1361円(税込、送料無料) (2023/2/17時点)

Tabulator セルのマウスオーバーで文字を反転表示

 

 

 

マウスが乗ったセルを反転させて強調表示させるため、組込みの関数を探してみましたが、ないようなので、StackOverFlowの投稿を参考にして、cellのformatterでエレメントにCSSのクラスを追加したり削除したりさせてみました。

< コード例 >
CSS


  /* tabulator セル文字反転用 */
  .text_reverse {
    color: white;
    background-color: black;
    
  }

Javascript


    // Airport name //
    {title: 'Name', field: 'nmAirport', width: 180,
      formatter: function (cell, formatterParams, onRendered) {

        // 反転させて強調 //
        const cellelm = cell.getElement()
        $(cellelm).on("mouseover", function (e) {
          $(this).addClass("text_reverse")

        })
        $(cellelm).on("mouseout", function (e) {
          $(this).removeClass("text_reverse")
        })
        return cell.getData().nmAirport

      }

    },

< 画面例 >

 

 

 

 

 

画像には枠をつけてみました。

 

 

 

 

 



  /* tabulator 画像セル背景反転用 */
  .imgback_reverse {
    background-color: #B8E2E6;
    border: 1px solid #00a0a0;
  }

Tabulator topCalc ビルトイン関数編

 

 

 

TabulatorのtopCalcプロパティを使えば集計値の表示が出来ます。
ビルトインで用意されたプロパティを使ってみました。
公式ドキュメントはこちら
sum, avg, count, maximum, minmum, concatenate が使えます。、

< コード例 >

sumとcount


        // 機材数 //
        {
          topCalc: "sum",
          title: "Fleet",
          field: "fleet_size",
          hozAlign: "right",
          width: 46,
          formatterParams: {thousand: ","},
          formatter: function (cell, formatterParams, onRendered) {
            if (cell.getValue() <= 0) {
              return ""
            }
            else {
              return numeral(cell.getValue()).format("#,###")
            }
          },
          //width: 50,
          sorter: "number",
          cssClass: "cell_num_onlytext_navy"
        },
        
        // 旅客貨物定時国際タイプ記号列挙 //
        { topCalc: "count",
          title: "Type", field: "type", hozAlign: "center", width: 50
        },

< 画面例 >

Tabulator topCalc カスタム自作関数編

 

 

 

Tabulatorで集計行の表示が、topCalc で簡単に出来て使ってましたが、ビルトインのプロパティにない集計が必要になり作ってみました。
公式ドキュメントはこちら

Group byした結果のアイテム数
メジアン
0や空白を除外した集計
などです。

< コード例 >
実は別関数にしたところ、うまく動かないので、カラムのプロパティに記述してます。

0以下除外平均


topCalc: function (values, dtoata, calcParams) {

  // 0 (データは-1)を除外した平均 //
  let cnt = 0
  let sum = 0
  values.forEach(function (value) {
  
    /*console.log("--- value")
     console.log(value)
     console.log("--- data")
     console.log(data)*/
  
    let val = value === -1 ? 0 : value
  
    if (val > 0) {
      cnt++;
      sum = Number(sum) + Number(val)
    }
  })
  //alert(sum + ":" + cnt)
  
  if (sum > 0 && cnt > 0) {
    const res = Math.round(sum / cnt * 10) / 10
    return res;
  }
  else {
    return 0
  }

}

0以下除外メジアン


topCalc: function (values, data, calcParams) {

  // 0 -1を除外したメジアン //
  let targets = []
  values.forEach((value) => {

    console.log(value)
    //let val = value.replaceAll(",", "")

    let val = value < 0 ? 0 : value
    val = parseInt(val)

    if (val > 0) {
      targets.push(val)
    }
  })
  console.log(targets)

  const half = Math.floor(targets.length / 2);

  if (targets.length % 2) {
    return targets[half];
  }
  else {
    return (targets[half - 1] + targets[half]) / 2;
  }

}

Group by アイテム数


topCalc: function (values, data, calcParams) {

  let manuCnts = {}
  values.forEach((tmp) => {

    manuCnts[tmp] = (manuCnts[tmp] || 0) + 1;

  })
  console.log("--- manufac")
  console.log(manuCnts)

  return Object.keys(manuCnts).length + " Manufactures"
}

< 画面例 >

Tabulator addRow()で行を追加した時の位置が上と下があるので

Tabulator addRow()で行を追加した時、原因はわかりませんが、既存の表の一番上に挿入されたり、一番下に追加されたりするので、戸惑いそうなので、上下線を入れて強調させてみました。

< コード例 >
CSS


.border-bottom-red1 {
    border-bottom: solid 1px red
}

.border-bottom-red2 {
    border-bottom: solid 2px red
}
.border-top-red1 {
    border-top: solid 1px red
}

.border-top-red2 {
    border-top: solid 2px red
}

Javascript


  // @@ テーブルタイトル右側 +アイコンを押した時 @@ //
  // ++ 追加 ++ //
  $(document).on("click", "#addarow", function (e) {
    //alert ("plus button clicked")

    // table行追加 //
    table.addRow({NM_GROUP: userGroup, DT_DEP: TODAY_FORMAT, ID_USER: userId, NO_FLIGHT: "NH____",
      IS_NOTIFY: 0, DEP_IATA: "", ARV_IATA: ""}, true)
    //table.selectRow([-1]);

    $("#savearow").removeClass("dsp_none")
    $("#savearow").show()
    $("#cancelarow").removeClass("dsp_none")
    $("#cancelarow").show()
    
    // 行を強調 //
    // NH____の行を特定 //
    const rows = table.getRows()
    rows.forEach((row) => {
      const noflight = row.getData().NO_FLIGHT
      if (noflight === "NH____") {
        
        const elm = row.getElement()
        $(elm).addClass("border-bottom-red1")
        $(elm).addClass("border-top-red1")
        return
      }
    })

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


  })

< 画面例 >

Tabulator コンテキストメニュー

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

< コード例 >

HTML

        <!-- 空港テーブル本体 -->
        <div id="aptable" class="main_table"></div>
        <!-- コンテキストメニュー -->
        <div id="aptable_contextmenu" class="contextmenu-wrapper" style="display: none;">
            <div class="contextmenu-contents apcntxtdiv">
              
                <!-- 出発ライブ -->
                <ul id="cntxt_livemap_ul_arv" class="curpo apcntxt">
                    <img id="cntxt_livemap_icon_arv" src="Img/arrival_48_green.png" width="18" />
                    <span id="cntxt_livemap_arv">Show arrival LiveMap</span>
                </ul>
                <!-- 到着ライブ -->
                <ul id="cntxt_livemap_ul_dep" class="curpo apcntxt">
                    <img id="cntxt_livemap_icon_dep" src="Img/departure_48_blue.png" width="18" />
                    <span id="cntxt_livemap_dep">Show departure LiveMap</span>
                </ul>
                
                <!-- 出発ステータスリンク -->
                <ul id="cntxt_status_ul_arv" class="curpo apcntxt">
                    <img id="cntxt_status_icon_arv" src="Img/arrival_48_green.png" width="18" />
                    <span id="cntxt_status_arv">Open arrival status</span>
                </ul>
                <!-- 到着ステータスリンク -->
                <ul id="cntxt_status_ul_dep" class="curpo apcntxt">
                    <img id="cntxt_status_icon_dep" src="Img/departure_48_blue.png" width="18" />
                    <span id="cntxt_status_dep">Open departure status</span>
                </ul>
                
                
                <!-- SkyVector -->
                <ul id="cntxt_skyvec_ul" class="curpo apcntxt">
                    <img id="cntxt_skyvec_icon" src="Img/skyvector.png" width="18" />
                    <span id="cntxt_skyvec">Open SkyVector</span>
                </ul>
                <!-- Airnav -->
                <ul id="cntxt_airnav_ul" class="curpo apcntxt">
                    <img id="cntxt_airnav_icon" src="Img/airnav.png" width="18" />
                    <span id="cntxt_airnav">Open AirNav</span>
                </ul>
                 <!-- FlightPlan -->
                 <ul id="cntxt_fplan_ul" class="curpo apcntxt">
                    <img id="cntxt_fplan_icon" src="Img/flightplan_icon.png" width="18" />
                    <span id="cntxt_fplan">Open FlightPlan</span>
                </ul>
                 <!-- LiveATC -->
                 <!--<ul id="cntxt_liveatc_ul" class="curpo apcntxt">
                    <img id="cntxt_liveatc_icon" src="Img/liveatc.png" width="18" />
                    <span id="cntxt_liveatc">Open LiveATC</span>
                </ul>-->

            </div>
        </div>

カラムのセルコンテキストイベントから自作メニューを呼ぶ


    // 行番号 //
    {
      title: 'No.',
      field: 'rowno',
      formatter: 'rownum',
      frozen: true,
      width: 16,
      hozAlign: 'right',
      cssClass: 'cell_num',
      cellContext: function (event, cell) {
        //event - the click event object
        //cell - cell component

        const row = cell.getRow()

        $(".apcntxtdiv").show()

        // 通常の右クリックメニューを停止 //
        event.preventDefault()
        // 代わりに自作のメニューを表示 //
        showNoContextMenu(row, event.pageX, event.pageY)
      }
    },

自作メニュー


  /**
   *  No列コンテキストメニュー
   * @param {type} row 行オブジェクト
   * @param {type} posx ページX位置
   * @param {type} posy ページY位置
   * @returns {undefined}
   */
  const showNoContextMenu = (row, posx, posy) => {

    const cm = document.getElementById("aptable_contextmenu")
    
    // 行のデータ //
    const icao = row.getData().cdIcao
    const iata = row.getData().cdIata
    const city = row.getData().nmCity
    const apname = row.getData().nmAirport

    // 非表示をやめて、クリックした位置に表示する
    cm.style.left = (eval(posx) + eval(20)) + 'px'
    cm.style.top = (eval(posy) + eval(10)) + 'px'
    cm.style.display = ''

    // メニューのテキストを列の値で変更 //
    $("#cntxt_livemap_arv").text(iata + " Show arrival LiveMap")
    $("#cntxt_livemap_dep").text(iata + " Show departure LiveMap")

    $("#cntxt_status_arv").text(iata + " Open arrival status")
    $("#cntxt_status_dep").text(iata + " Open departure status")

    $("#cntxt_skyvec").text(icao + " Open SkyVector")
    $("#cntxt_airnav").text(icao + " Open AirNav")
    $("#cntxt_fplan").text(icao + " Open FlightPlan")
    $("#cntxt_liveatc").text(icao + " Open LiveATC")


    $(".apcntxt").show()

    $(".apcntxt").off("click")

    // @@ アイテムをクリックした時 @@ //
    $(".apcntxt").on("click", function (e) {

      cm.style.display = 'none'

      const id = $(this).attr("id")

      // == id別の処理 省略 == //
      


    })

  }

< 画面例 >

Tabulator テーブルの行IDを使えるようにするため

Tabulator で選んだ行などに対して処理を行いたい場合、一意に識別したい場合のIdの使い方です。

詳しくはこちら

< コード例 >

カラムに非表示のID列をセット

// 行ID //
{field: "id", visible: false},

行選択

// テーブル行選択 //
table.selectRow(0) //select row with id of 0

行Id取得

// 行ID取得 //
const rowidx = row.getIndex()

サーバー側

  // パラメータセット //
  $stmt = $dbh->prepare($SQL);
  $stmt->bindvalue(':MINDT', $mindt, PDO::PARAM_STR);
  $stmt->bindvalue(':MAXDT', $maxdt, PDO::PARAM_STR);
  $stmt->bindvalue(':GROUP', $group, PDO::PARAM_STR);
  $stmt->execute();

  $response = new stdClass();

  // 結果格納 //
  $i = 0;
  while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {

    $response->data[$i]["ID_BOOK"] = $row["ID_BOOK"];
    $response->data[$i]["NM_GROUP"] = $row["NM_GROUP"];
    
    // Tabulator行ID //
    $response->data[$i]["id"] = $i;

    // == 省略 == //

    $i++;
  }
} catch (PDOException $e) {
  var_dump($e->getMessage());
}

echo json_encode($response, JSON_NUMERIC_CHECK);