23
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【衝撃】DOM操作が辛いのでjsテンプレートエンジンを追加しようと思ったが、Pleasanterには既に実装されていた

Last updated at Posted at 2021-12-12

序文

DOM操作が辛かった。ただそれだけの動機だった。

フォルダにダッシュボードを実装するため、jQueryを駆使してあれやこれやのパーツを入れようと思ったが、テンプレートエンジンに慣れきった我が身にとって、それは苦痛を伴う作業であった。

やがて私は追い詰められ、パフォーマンスを犠牲にしてでも、jQuery.getScriptでCDNからテンプレートエンジンを読み込み、viewとロジックを(ある程度)分離した状態で開発できればとまで思い詰めるまで至った。

しかし試したライブラリはいずれも相性やパフォーマンスの影響があり、満足のいくものではなかった。1

深く絶望し、自暴自棄になった私の目前には、いつもの無機質なgoogle devtools。実装のヒントはなんて無いだろうと考えつつも、沈んだ瞳をスクリプトのロード付近に向けた。

その時、我が目を疑った。

あった。既に、そこにあったのだ。

Hogan.js が。

───

この記事はプリザンター(Pleasanter) Advent Calendar 2021 の参加記事だと思う。

Hoganとは、或いはテンプレートエンジンとは

Hoganはtwitterが開発した、mustache構文で書ける軽量テンプレートエンジンだ。

テンプレートエンジンとは、特定のタグで囲まれた箇所にデータを差し込むことで、UI部分とデータ部分を、わかりやすく分離することができる仕組みのこと。mustache構文の場合、{{ データ部 }} のように記述する。

UI表示に関わる制御もテンプレートエンジンで持つことができ、処理自体も簡潔に書けるメリットがある。また、エスケープ処理なども得意。

とはいえ、実際に見てもらう方がわかりやすいだろう。

実例1: テーブル一覧のDOM操作

Screenshot 2021-12-13 at 00-01-33 プリザンター.png

上記のようなテーブルを用意して、

Screenshot 2021-12-13 at 00-04-28 プリザンター.png

//これがデータ部
var context = {
    guide: '(hogan)エスケープが自動的に反映されるので、 <br>## 安全に文字列を処理することができます。',
    flg: true,
    tbl: {
        title: '(hogan)セルの中も、レンダリング可能。',
        content: '(hogan)表示されます。if制御もテンプレ内でできます。',
        comm: '(hogan)明示的にエスケープも<br>外せます。使用時注意!'
    }
};
var app = $('#Application');

//ここでDOM操作。on_grid_loadを使うためフィルタリセット後にも反映される
$p.events.on_grid_load = function () {
    let tmpl = Hogan.compile(app.html());
    app.html(tmpl.render(context));
};

スクリプト画面に、上記コードを差し込むと

Screenshot 2021-12-13 at 00-04-44 プリザンター.png

データ部のオブジェクトが書き込まれてロードされる。想像以上に高速軽快に動く。

実例2: ダッシュボードを実装する

フォルダの画面にダッシュボードを実装された方も多いだろう。自身もそこに、プリザンターで作成したブログアプリの一覧を表示させたいと考えた。APIからブログの摘要を取得し、並べていくパターンを想像してほしい。

標準のjQueryを用いたDOM操作であれば、APIから一括取得したデータを、任意の条件のもとで、mapやforを使い、データを整形し、それを.append()やら.html()やらで……となるだろう。jQueryつよつよエンジニアでもない限り、見やすく処理を書くのは至難だろう。

だが、もっと簡潔に書ける。そう、Hoganならね。

//アクサンで囲む変数記法はヒアドキュメントというES6からの実装。これを使う時点でIE対応は諦めている
var tp_on_grid_load = `
<div id="RightMenu" class="pure-u-1-1"> <!-- pure.cssのクラスについては後述 -->
    <h2>{{name}}</h2>
    <table class="pure-table pure-table-horizontal"> <!-- pure-tableで読みやすいテーブルにする -->
        <thead>
            <tr class="ui-widget-header">
            <th>更新日</th>
            <th>タイトル</th>
            <th>本文</th>
            <th>掲載終了日</th>
            </tr>
        </thead>
        <tbody>
            <!-- ここからループ処理に入る。配列blogsに格納されたオブジェクトを回してレンダリング -->
            {{#blogs}}
                <tr class="grid-dash" data-id="{{id}}" data-ver="0" data-latest="1" >
                <td>{{update}}</td>
                <td><a href="/items/{{id}}/edit">{{title}}</a></td>
                <td>{{body}}</td>
                <td>{{end}}</td>
                </tr>
            {{/blogs}}
        </tbody>
    </table>
</div>
`;

//他のスクリプトとの兼ね合いで遅延実行させたかったので囲っている
(function () {
    let tmpl = Hogan.compile(tp_on_grid_load);

    //fetch APIでテーブル(ID:13)を全取得。実際は適宜条件を入れ、必要な情報だけを引き出すようにすべき
    fetch($p.apiUrl(13,'get'), {method: 'post'})
        .then(response => response.text()) //responce.json()でいいのでは?と思うが、私もそう思う
        .then(data => {
            const jp = JSON.parse(data);
            if(jp.StatusCode != 200){ return false; }
            //ここでmapを回し、配列を作成
            const aftermap = jp.Response.Data.map(item => (
                { 
                    'id' : item.IssueId, 
                    'update' : item.UpdatedTime, 
                    'title' : item.Title, 
                    'body': item.Body, 
                    'end': item.CompletionTime 
                }
            ));
            $('#MainForm').append(tmpl.render(
                { 
                    name: '連絡事項ブログ(休日引継ぎなど)', 
                    blogs : aftermap
                }
            ));
    });
}());

上記処理ではテンプレート部でLoop処理を使っている。{{#blogs}}で囲まれたあたりがそれだ。APIなどの複数の構造化データを表示させるケースにおいては、UIの可読性と処理速度、両方を改善させることができるため、非常に強力。

それをスクリプトに反映すると、こうなる。
(グラフは別のスクリプトからc3.jsを使って読み込んでる。ここでは説明しない)

Screenshot 2021-12-13 at 00-26-37 プリザンター.png

つまり「スクリプト」が追加できる箇所であれば、Hoganで容易にDOM操作をすることができる。そのため「フォルダ」では使えるが「Wiki」では使えない。2

注意事項

ここまで優れた機能が、なんと追加のスクリプト読み込みなしで使えてしまう! 素晴らしい……。

だが、公式ドキュメントにはHoganの記述は全くない。3

そのため、特にサポート契約を締結しているクライアントの環境で、Hoganを利用したシステムにて、なにか質問や不具合が発生しても、その点はサポート範疇外になることが予想される。自己責任の上で利用するべきだ。

また、特段の理由がない限り、アンエスケープは使わないこと。思わぬセキュリティリスクを発生してしまうこともあるためだ。

おまけ1: hogan.jsはいつ実装されたのか

公式リポジトリ参照。
https://github.com/Implem/Implem.Pleasanter/commit/bc4576cb59c6250df17327b491f3e29ec3df4d3b

私はC#読めないのでissue投げることもできないのですが、これ本体内部で使われてます……? 他のソースでrender()とかされてなさそうだったので気になっております。

おまけ2: 実例2のぴゅあなCSSについて

Yahoo!が開発するド軽量cssフレームワークpureだ。
この手のものはcssリセットがついてくるため、他CMSとの同居が躊躇われるが、pureはモジュール毎のロードが可能なため、pleasanterのデザインと同居する形で使える。4

//わかり易さ・実装のしやすさから、javascriptでcss読み込みを実装する例を掲示。これで合計1.3kb。
//だが実際には拡張HTMLの HtmlHeaderTop_ja.html に読み込んだ方がパフォーマンス的に良い。
$('head').append('<link rel="stylesheet" href="https://unpkg.com/purecss@2.0.6/build/grids-min.css">');
$('head').append('<link rel="stylesheet" href="	https://unpkg.com/purecss@2.0.6/build/tables-min.css">');

(function () {
    //こちらもClassを後付で上書きする例だが、サーバスクリプトが使えそうな要素なら、それを利用すると良い。
    //pure gridは他と比べても柔軟性が高く、最大24gridまで組める。
    //また、5等分も組める数少ないグリッドシステムである。花嫁かよ
    $('#MainForm').addClass('pure-g');
    $('#SiteMenu').addClass('pure-u-1-5');
}());

古い情報で良ければ私の記事も参考になるだろう。

BootStrapより軽くて少機能! CSS Framework「Pure」をBootStrap Advent Calendarで宣伝する暴挙に出る!

おまけ3: 著者とPleasanterについて

実はローコード開発ツールの調査中で、まだPleasanterは選定中の段階である。

とはいえUI/UX知識があればかなりのカスタマイズが可能な点や、充実したAPIによる他システムの連携などに魅力を感じており、選定が通り次第、サポート企業にご相談させていただければと思う。

  1. ただ単に技術力がないだけという説も。

  2. 本音を言うと一番Wikiで使いたい機能なのだが……。

  3. 「プリザンター hogan」でgoogle検索しても情報が皆無なので、この記事が初出。アイエエエ!

  4. もっとも、pureもpleasanterもnormalize.cssでcss resetしている為、相性が良いのは確定的に明らか。

23
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
23
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?