まだプログラマーですが何か?

プログラマーネタとアスリートネタ中心。たまに作成したウェブサービス関連の話も http://twitter.com/dotnsf

タグ:select

HTML の <select> 要素は複数の選択肢から1つを選択させるためのパーツです。特にスマホなどでは入力の負担が大きいため、入力内容が長いケースでは選択肢の中から選ぶことができると利用者の負担を軽くすることができて便利です。

ただ必ずしも全てのケースで「選択肢から選ぶ」ことが可能とは限りません。めぼしい選択肢を用意した上で、それでも選択肢以外の答を入力したい/させたい場合、多くのケースでは「その他」という選択肢を用意した上で、「その他」を選んだ場合のみ、詳しい内容を別のテキストフィールドに入力させる、という方法が取られているように感じます。 これはこれで(最低限の目的を達成することができるという意味で)いいのですが、UI/UX の観点では例外的なパーツ処理となり、見た目にもスマートではありません。

HTML を使わないネイティブアプリケーションなどでは「任意文字列も入力可能なセレクトボックス」のようなパーツも用意されていて、これを使うことで選択肢の中から選ぶことも(ダブルクリックするなどして編集状態に切り替えた上で)任意文字列を入力することもできます。これだと見た目もスマートでいいですよね。というわけで、このようなパーツを HTML でも用意できないか挑戦してみました。目標はこのブログエントリのタイトルでもある『ダブルクリックすると <input> になって任意入力可能な <select> 』の実現です。

説明の前に、まずは実際に動くサンプルを使ってみてください。下の <select> から値を選び、"Value" ボタンをクリックすると、選択されている値が alert() で表示されます。<select> 部分をダブルクリックすると編集可能になって任意文字列が入力可能になり、その上で "Value" ボタンをクリックすると、入力されている値が alert() で表示される、というものです:




そんなに特別なことをしているわけではないのですが、以下解説です。まず HTML での該当箇所は以下のようになっています:
<select id="mySelect">
  <option value="12345">12345</option>
  <option value="23456">23456</option>
  <option value="34567">34567</option>
  <option value="45678">45678</option>
  <option value="56789">56789</option>
</select>
<input type="text" id="myInput" style="display:none;" value=""/>

<select>(id = "mySelect")と <input>(id = "myInput")を1つずつ配置しています。ただし <input> には style="display:none;" を指定して非表示にしています。つまりこの時点では <select> のみが表示されています。先にコツを言っておくと、<select> の値は常に <input> に引き継がれるように(この後で)設定しておき、"Value" ボタンクリック時には <select> ではなく <input> の値を取得するようにします。

次に JavaScript の解説です:
$(function(){
  //. select の値が変わった時のハンドリングを定義
  $('#mySelect').change( onSelectChange );
  onSelectChange();

  //. select がダブルクリックされたら select を非表示にした上で、同じ値が入っている input を表示する
  $('#mySelect').dblclick( function( e ){
    $('#mySelect').css( 'display', 'none' );
    $('#myInput').css( 'display', 'block' );
  });
});

//. select の値が変わったら、その値を隠しフィールドに代入しておく
function onSelectChange(){
  var v = $('#mySelect').val();
  $('#myInput').val( v );
}

まず画面ロード時に <select> の値が変わった時の処理を定義しています。ここでは <select> の値が変わるたびに onSelectChange() が実行され、<select> で選択された値が常に <input> の値として反映されるようにしています(またロード直後に1回実行することで <input> を初期化しています)。

また <select> がダブルクリックされた時の処理も記述しています。ダブルクリックされたら <select> を非表示に、<input> を表示状態に切り替えます。<input> には上述の処理によって常に <select> で選択されていた値が入っているので、その値が引き継がれた状態で編集可能なフィールドとして表示されることになります。

加えて、以下のスタイルシートを適用することで <select> と <input> が同じサイズになるように設定し、ダブルクリック時に <select> が自然に <input> に切り替わったように見えるよう調整しています:
#mySelect{
  height: 40px;
  width: 200px;
}
#myInput{
  height: 40px;
  width: 200px;
}

これでダブルクリックすると <input> に切り替わる <select> を作ることができました。 あとは "Value" ボタンですが、これは onClick 属性に以下のような関数を定義して、<input> に設定された値を取り出すようにしています:
<input type="button" class="btn btn-primary" value="Value" onClick="getValue();"/>
//. 現在の値を取り出す
function getValue(){
  //. select ではなく input に入っている値を取り出す
  var v = $('#myInput').val();
  alert( v );
}

実際のサンプルコードは github で公開しているので、こちらも参照ください:
https://github.com/dotnsf/select2input

 

Node-RED だけで SQL Database(DB2) 内のデータを検索して結果を表示する、という仕組みを作ってみます。

IBM Bluemix などから提供されているオープンソース・データフローエディタである Node-RED は、ノードと呼ばれる機能単位のブロックを組み合わせて、ほぼコーディングをすることなくデータのワークフローを作れるウェブアプリケーションです。そのフローにデータベースを組み合わせて問い合わせしたり、結果を格納したり、といったことも少ない(或いはほとんどない)コーディングで実現できる、という便利なツールです。特に Bluemix と組わせることで IBM IoT Foundation と連携した MQTT システムを簡単に構築することが可能になります:
2016020101


データのフローを得意とする一方で、Node-RED 単体では UI はあまり得意ではありません。全くできないわけではないのですが、基本的には HTML などの知識を前提として、動的に HTML を作成するような形になります。むしろサーブレット的というか、UI を外出しした仕組みの1パーツとして利用するのが便利だと思っています。


例えば、現在 SQL DB 内に MYTABLE というテーブルが作られ、以下の様な状態になっているものとします。ここから ID 値が 'fff57f93.000a8' であるデータを検索する、ということを目的としてみます。この ID はパラメータとして与えるようにして、リクエストの度に変更できるようなものにします:
2016020101


普通に SQL を使うのであれば(言語によって JDBC などの設定をした上で)、SELECT * FROM MYTABLE WHERE ID = 'fff57f93.000a8' といった具合にクエリーを実行して結果を得ることになると思います。

この処理を NodeRED で行うにはどうすればよいでしょうか?そのためにはまずこの SQLDB に NodeRED からアクセスできるようになっている必要があります。Bluemix 環境であれば、目的の NodeRED プロジェクトに、この SQLDB を事前にバインドしておきます(下図ではサービス名が SQL Database-iv となっています。適宜みなさんの実際の環境と読み替えてください):
2016020101


バインドした上で NodeRED のパレット内の SQLDB を使います。なお、ここでの SQLDB ノードは(検索をしたいので)左右両方にノード接続点があるものを使います。
2016020102


そして以下の様な NodeRED データフローチャートを作ります。HTTP Request ノード、SQL DB ノード、JSON ノード、Template ノード、HTTP Response ノード、そして Debug ノードの6つを以下のようにつなぎます。以下、各ノードの属性を説明します:
2016020101


HTTP Request ノードでは Method として GET、そして URL として /query を指定します。この URL で指定した内容が実際に問い合わせをする際の URL になります:
2016020102


SQL DB ノードでは問い合わせ処理を記述します。Service では SQL DB のサービス名をプルダウンから選択し、Query には ID をキーに MYTABLE テーブルを検索したいので select * from MYTABLE where id = ? と入力します。そしてこの ? パラメータに代入される値を直前の HTTP Request ノードの URL パラメータから取得するようにします(Prepared Statement っぽい感じ)。HTTP Request ノードの URL パラメータの値は msg.req.query で取得できます。今回は id というパラメータにこの値が入っているよう想定するので、Parameter Markers の欄に msg.req.query.id と指定します:
2016020103


JSON ノードは JSON ブロックをそのまま配置するだけです(名前は適当に指定してください):
2016020104


そして Template ノードで検索結果を表示用に整形します。ここでは HTML 形式で、背景を薄い黄色にして表示してみましょう。以下の様な内容を入力します。HTML 内の {{payload}} 部分に検索結果が入り、その結果を msg.payload として返す、という指定をしていることになります:
2016020105


最後に HTTP Response ノードと debug ノードをそれぞれ配置します。この状態でフローを Deploy します:
2016020106


そしてウェブブラウザで、http://(Node-RED のあるサーバー)/query?id=(SQL DB から検索したいレコードのID値) を指定してアクセスします。成功すると指定した ID 値を持つレコードが JSON テキスト形式で、指定したフォーマット(背景が薄い黄色)で表示されるはずです:
2016020107


URL パラメータの id の値を別のレコードのものに変えると、指定したレコードの内容に変わるはずです:
2016020108


同時にこれらの検索結果は Node-RED 画面の debug タブ内にも表示されているはずです:
2016020109


上記例では結果を(わざわざ)HTML 化して出力しましたが、現実には JSON のまま返したり、XML 化して返す、といったサーブレット的な処理をする機会が多いと思います。要は見た目については Node-RED の外で管理すべきと考えています。


・・・と、そう思っていたのですが、最近になって Node-RED に UI パーツを追加できるようなノード部品が提供されたようです。ある程度用途は限られていますが、UI も Node-RED で作れるようになっていくのかもしれません:
https://github.com/andrei-tatar/node-red-contrib-ui


とりあえず、Node-RED だけでサーブレット的な検索機能を実装することもできそうだ、ということが分かりました。


このページのトップヘ