2018年4月16日月曜日

Sinatra + jQuery で UI の制御をしてみる

概要

前回 Sinatra + bulma で Sinatra アプリにデザインを当て込む方法を紹介しました
bulma はボタンなどの UI コンポーネントを a タグで定義するケースが多いです
form タブが使えないわけではないですがせっかく用意された bulma のコンポーネントは使えません
今回は a タグで定義したボタンから jQuery (Ajax) を使って POST リクエストを送信して Sinatra 側で処理して UI 側で結果を表示してみたいと思います

環境

  • macOS 10.13.2
  • Ruby 2.4.1p111
  • sinatra 2.0.1
  • bulma 0.6.2
  • jQuery 3.3.1

jQuery のインストール

ローカルに .js ファイルをダウンロードしてもいいですが jQuery CDN という .js ファイルを配信してくれている CDN サーバがあるのでそれを使います

テンプレートファイルの head タグ内に jQuery の情報を定義しましょう

  • vim views/admin.erb
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Admin - Free Bulma template</title>
  <link href="https://fonts.googleapis.com/css?family=Open+Sans:300,400,700" rel="stylesheet">
  <link rel="stylesheet" type="text/css" href="../css/admin.css">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.2/css/bulma.min.css">
  <script defer src="https://use.fontawesome.com/releases/v5.0.6/js/all.js"></script>
  <script type="text/javascript" src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
</head>
<body>

... 以下省略

とりあえず head タグの最後に追記しました
今回 Ajax を使います
jQuery CDN に slim minified というタイプの jQuery がありますがこれだと Ajax 関数が用意されていないので minified というタイプを使うようにしてください

テンプレートファイルの編集

すでに用意されている a タグを変数して jQuery でタグの情報を取得できるようにします

155 行目くらいにある a タグで用意されたボタンがあるのでそこに属性を追加します

  • vim views/admin.erb
<table class="table is-fullwidth is-striped">
  <tbody>
    <tr>
      <td width="5%"><i class="fa fa-bell-o"></i></td>
      <td>Lorum ipsum dolem aire</td>
      <td><a class="button is-small is-primary" id="action1" data-value="action1_data">Action</a></td>
    </tr>
    <tr>
      <td width="5%"><i class="fa fa-bell-o"></i></td>
      <td>Lorum ipsum dolem aire</td>
      <td><a class="button is-small is-primary" id="action2" data-value="action2_data">Action</a></td>
    </tr>

... 以下省略

id="action1" data-value="action1_data" という属性を追加しています
とりあえず 2 つの a タグに追加しています
HTML5 に data-* というカスタム属性がありこれを使うと好きな名前の属性をタグに追加することができます
今回は data-value というカスタム属性を追加しています

独自 js ファイルの作成

先ほど追記したカスタム属性を操作できる JavaScript ファイルを作成します
当然ですがここで jQuery を使っていきます

  • vim public/js/form.js
$("a[id^='action']").on("click",function() {
    var value =  $(this).data("value");
    console.log(value);
    $.ajax({
      type: "POST",
      url: "/action",
      data: {
        "value": value
      }
    }).then(
      // success
      function (data) {
          alert(data);
      },
      // error
      function (edata) {
          console.log(edata);
          alert(edata.responseText);
      }
    );
})

まずボタンがクリックされたかの判定をセレクタの正規表現を使って判断しています
a タグに設定された id タグが action で始まるタグがクリックされた場合に処理が実行されるようにしています

  • $("a[id^='action']").on("click",function() {

そして data-value で定義したカスタム属性の値を取得しています

  • $(this).data("value");

次にこれを Ajax を使って送信しています
Ajax の送信先は Sinatra になります
Sinatra 側のルーティング定義はこのあと追加します
フォームを想定して POST で送るようにしてみました
カスタム属性で取得した値をそのまま送信しています

あとは成功時と失敗時の処理を then を使って定義しています
最近の jQuery では then という構文が使えるのでそれを使ってモダンな感じで書いています
もちろん success, error を使って書いても問題なく動作はします

作成した form.js はテンプレート内から参照しておいてください
参照箇所は body タグの直前になります

  • vim views/admin.erb
... 省略

  <script async type="text/javascript" src="../js/form.js"></script>
</body>
</html>

Sinatra アプリケーションの修正

では最後に Ajax から送られたデータを受け取るルーティングを追加します
以下のルーティングを追加しましょう

  • vim app.rb
post '/action' do
  "ok -> #{params['value']}"
end

処理は特に何もしていません
送信されてきた値に ok という文字列を付与して返しているだけです
これを受け取った jQuery 側はこの文字列情報をアラートに表示します

動作確認

  • bundle exec ruby app.rb

で localhost:4567 にアクセスし該当のボタンをクリックしてみましょう
成功するとアラートが表示されると思います
sinatra_bulma_jquery_sample1.gif

ちなみに Ajax のエラー側の処理をさせたい場合は Sinatra アプリのレスポンスをエラーになるように変更してあげれば OK です

post '/action' do
  status 400
  "ng -> #{params['value']}"
end

最後に

Sinatra に jQuery を組み込んで UI 側の制御もできるようにしてみました
Sinatra アプリ側で生成した HTML を返却してあとはフロントサイド側 (JavaScript) だけで処理をしてあげればサーバ側へのアクセスも減るので軽量な UI を実現できるかなと思います

今回は主に Ajax のサンプルを紹介しましたが同じように DOM の操作も行えるのでクリックした際に新たに div タグを生成するなどすればインタラクティブな UI を実現することができると思います

0 件のコメント:

コメントを投稿