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

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

タグ:html

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

 

比較的新しい Web API の1つである MediaDevices インターフェースを使って、HTML と JavaScript だけで(PCの)画面共有が実現できるようになりました。とりあえず使ってみるには PC のウェブブラウザ※で以下のサイトにアクセスしてみてください:
https://dotnsf.github.io/display_media_stream/

※対応ブラウザはこちら: 
https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getDisplayMedia#Browser_compatibility



対応ブラウザで上記 URL にアクセスすると、画面共有のダイアログが表示されます:
2020033101


何かひとつ(下図の例では「アプリケーションウィンドウ」タブの「タスクマネージャー」)選択して、「共有」ボタンをクリックすると、、
2020033102


選択したアプリケーションのウィンドウ内画面がストリームでウェブブラウザに表示されます。最初に「全画面」を選んでいればウィンドウ全体がブラウザ内に表示されます:
2020033103


この機能を停止するには共有ダイアログの「共有を停止」ボタンをクリックしてください:
2020033104


以下、上記デモのソースコードを紹介します。コードそのものはこちらで公開しています:
https://github.com/dotnsf/display_media_stream


上記デモは実質的に index.html ファイルだけで実現しています。つまり HTML と JavaScript だけで実現しています。まず HTML 部(<body>部)は以下の通り、かなりシンプルです:
<body>
  <!-- Video element (live stream) -->
  <div>
    <video autoplay playsinline id="video" width="640" height="480"></video>
  </div>
</body>

<div> 要素の中に <video> 要素が1つだけ、autoplay 属性と playsinline 属性がついた状態で存在しています。一応初期サイズの指定もしていますが後で JavaScript で修正します。

次に肝となる JavaScript 部はこちらです:
<script>
var localVideo = null;

function gotLocalMediaStream( mediaStream ){
  localVideo.srcObject = mediaStream;
}

function handleLocalMediaStreamError( error ){
  console.log( "navigator.getUserMedia error: ", error );
}


$(function(){
  //. 画面サイズ取得
  var sw = window.parent.screen.width;
  var sh = window.parent.screen.height;
  //sw : sh = x : 480; => x = 480 * sw / sh;
  var x = Math.floor( 480 * sw / sh );
  $('#video').css( { width: x } );

  var mediaStreamConstraints = { video: true };
  localVideo = document.querySelector( "video" );
  navigator.mediaDevices.getDisplayMedia( mediaStreamConstraints ).then( gotLocalMediaStream ).catch( handleLocalMediaStreamError );
});
</script>

実質的には $(function(){ ... }); 部分が最初に実行されます。まずは window.parent.screen にアクセスして実画面のサイズを取得し、その縦横割合に合わせて上述の video 要素をリサイズ(縦は 480 に固定して、横を同割合になるようリサイズ)します。

そしてこの画面共有を実現しているのはこの1行です:
  navigator.mediaDevices.getDisplayMedia( mediaStreamConstraints ).then( gotLocalMediaStream ).catch( handleLocalMediaStreamError );

mediaDevices インターフェースの getDisplayMedia メソッドを、{ video: true } というオブジェクトを引数に実行しています。これがカメラやマイクではなくディスプレイ画面のストリームを取得するための処理で、成功すると gotLocalMediaStream 関数がコールバックされます。

その gotLocalMediaStream 関数は以下のような内容になっています:
<script>
var localVideo = null;

function gotLocalMediaStream( mediaStream ){
  localVideo.srcObject = mediaStream;
}

getDisplayMedia メソッドで取得したメディアストリームを引数にコールバックされ、その値を video 要素として取得済みの localVideo 変数の srcObject 属性に代入しています。これだけで後はディスプレイ画面の動画ストリームが video 要素の中で自動再生(autoplay)されます。


mediaDevices インターフェースや getDisplayMedia メソッドがまだ限られたブラウザ(Chrome, FireFox, Edge)の比較的新しいバージョンでしか使えず、スマホ系ブラウザでは全滅という状況ではあるのですが、ネイティブアプリを使わなくてもブラウザの JavaScript だけでここまでできるようになっていたんですね。video 要素として使えるということはスクリーンショットとかいろいろ応用できそう・・・


むかーしから存在している技術なのですが、イメージマップという便利な機能があります(最近あまり使われなくなったという印象もあります)。これは HTML ページ内の画像内にクリッカブルな領域を複数定義し、いずれかの領域がクリックされたら何らかの処理を行う、というものです。クリックされる領域によって処理内容(このページにジャンプするとか、この JavaScript 関数を実行するとか、・・)を変えることができる、というものです。

例えばこんな感じで実現します。いらすとやさんの『ホワイト企業~ブラック企業のイラスト』を例に紹介します:



このイラスト画像は横650ピクセル、縦137ピクセルです。その中にホワイト企業~ブラック企業が5段階のイラストで表示されています。

画像の左上の座標を ( 0, 0 ) とすると、このうち一番左の企業は、そのビルの矩形部分は左上が ( 37, 13 ) から右下 ( 100, 127 ) という矩形でできています。いま、このビルの矩形部分がクリックされたら "white" というメッセージを表示するようにしてみます:
2019100701


同様にして、5つのビルそれぞれにクリッカブルな矩形部分を定義し、それぞれがクリックされた時に以下のようなメッセージが表示されるようにしてみます:
ビル番号(左から何番目のビル)矩形範囲クリックされた時のメッセージ
1 ( 37, 13 ) - ( 100, 127 ) white
2 ( 165, 13 ) - ( 227, 127 ) lightgray
3 ( 291, 13 ) - ( 355, 127 ) gray
4 ( 481, 13 ) - ( 483, 127 ) darkgray
5 ( 546, 13 ) - ( 610, 127 ) black


これをイメージマップで実現すると、 HTML の該当箇所は以下のようになります:
<body>
  <div class="container">
    <img src="https://1.bp.blogspot.com/-BmJohMucVBI/XYhOV_VMQmI/AAAAAAABVHA/sZF8lMjPedUWSkxBwUGZXmVri2OFKEZ4gCNcBGAsYHQ/s650/company_white_black_kigyou_5dankai.png" border="0" usemap="#image_map"/><br/>
    <map name="image_map">
      <area shape="rect" coords="37,13,100,127" href="javascript:alert( 'white' )"/>
      <area shape="rect" coords="165,13,227,127" href="javascript:alert( 'rightgray' )"/>
      <area shape="rect" coords="291,13,355,127" href="javascript:alert( 'gray' )"/>
      <area shape="rect" coords="418,13,483,127" href="javascript:alert( 'darkgray' )"/>
      <area shape="rect" coords="546,13,610,127" href="javascript:alert( 'black' )"/>
    </map>
  </div>
</body>

<img> タグの usemap 属性でイメージマップの名称を定義します。そして <map> タグを使って、対象(name 属性で指定)の <img> のクリッカブル領域と、クリックされた時のハンドラを href 属性に定義します。上記例ではハンドラを javascript 関数の実行にしていますが、普通に URL を記述すればリンクを作成することもできます。

これで各ビルをクリックした時に、(左から順に)"white", "lightgray", "gray", "darkgray", "black" というメッセージが表示されるようになります。元は1枚の画像でしたが、クリッカブルな領域を5箇所定義することができ、それぞれのクリックハンドラを個別に設定することもできました:

(左から2番めのビルをクリックした時の様子)
2019100703


ここまでが普通のイメージマップです。このイメージマップ、非常に便利な反面で「レスポンシブ対応が難しい」という難点がありました。要はクリッカブルな矩形領域をピクセル絶対値で指定しているため、画像がそのまま表示された場合はいいのですが、レスポンシブ対応などで画面サイズに合わせて拡大縮小されて画面いっぱいに表示された場合、縮尺が変更になってしまい、絶対値で指定していたイメージマップの定義がおかしくなってしまうのでした。

具体的に同じコードをスマホのシミュレーターで確認した結果、下の赤点線部が "lightgray" のクリッカブル矩形部分となっていて、本来の位置からズレていました。これをどうにかしたい、というのが今回のテーマです:
2019100702


色々と調べましたが、答としては jQuery RWD Image Maps という jQuery プラグインを使うことで解決できました:
2019100700



以下に具体的な対応手順を紹介します。

まず HTML 内でレスポンシブのための宣言をしておきます。例えば以下のようなコードが既に含まれているものと仮定します:
  :
<meta name="viewport" content="width=device-width,initial-scale=1"/>
  :


次に jQuery と jQuery RWD Image Maps をロードします。以下の例では CDN からそれぞれをロードしています(jQuery RWD Image Maps の方を後にロードする必要があります):
  :
<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jQuery-rwdImageMaps/1.6/jquery.rwdImageMaps.min.js"></script>
  :

CSS で画像サイズの調整を行います。この例ではイメージマップを用いる画像の横幅を 100% に、高さは自動調整するように指定しています:
  :
<style>
img[usemap]{
  max-width: 100%;
  height: auto;
}
</style>
  :

そして最後に JavaScript で該当部分に RWD Image Maps を適用します:
  :
<script>
$(function(){
  $('img[usemap]').rwdImageMaps();
});
</script>
  :

これでスマホでおなじページにアクセスした時でもイメージマップの矩形部分がズレることなく利用できるようになりました:
2019100704




Node-RED Advent Calendar 2017 に参加しました(ちなみにアドベントカレンダーに参加するのは初めて)! 12月9日のネタは「Node-RED で(普通の)ウェブアプリケーションを作る」です。

Node-RED を早くから活用している方の多くは IoT 連携であったり、組み込み機械との連携のデータフローとして使っているケースが多いと思っています。でもそんなケースばかりではなく、自分自身は Node-RED でデータベースを読み書きする REST API だけを作ったり、その REST API を呼び出すフロントエンドまでも含めて Node-RED で作ることがあります。今日はその作り方をスクリーンショットを交えて紹介します。


【普通のウェブアプリケーション】
最初に、今回作成する「(普通の)ウェブアプリケーション」を定義します。ここでの「ウェブアプリケーション」とは「アプリケーションサーバーとデータベースサーバーから構成」されていて、「アプリケーションサーバーのユーザー画面を通じて、データの保存、読み込み、検索、・・といった処理ができる」ものとします:
2017111702


これを Node-RED で作ります。具体的にはアプリケーションサーバー部分を Node-RED として、外部のデータベースサーバーに対する読み書きを行う API を Node-RED 上に定義します。そしてそれらの API を使う UI となる HTML も Node-RED で作ることで、ウェブアプリケーションとしての機能を実現します。なお今回はデータベースサーバーに IBM Cloudant を使い、全ての機能を IBM Cloud 上に作成することにします:
2017111901


【環境準備】
というわけで、まず Node-RED を用意します。Node-RED 環境が既に手元にある方は読み飛ばしていただいてもいいのですが、この後 IBM Cloudant を利用するので IBM Cloud のアカウントを取得し、IBM Cloudant のサービスインスタンスを用意するようにしてください(まあそこまでやるなら Node-RED も IBM Cloud で作っちゃうのがいいと思います)。

Node-RED はローカルで作ってしまってもいいと思いますが、このあとデータベース連携を行うので、(IBM Bluemix 改め)IBM Cloud の Node-RED 環境を使うことにします。無料のライトプランで契約している場合は Internet of Things Platform Starter ボイラープレートからランタイムを作成してください:
2017111701


ランタイムが稼働するまでしばらく待ちます。このボイラープレートから作成すると Node-RED の(Node.js の)ランタイムに加えて、Cloudant データベースのサービスインスタンスも同時に使えるようになります:
2017111701


実際に Node-RED を使おうとすると、初回のみアクセス用の ID とパスワードの設定を求められます。推測しにくいものを指定して、設定してください:
2017111702


改めて作成したアプリケーションの URL を指定し、Node-RED の画面になったら "Go to your Node-RED flow editor" をクリックしてフローエディタ画面に移動します(この際に認証が求められるので、直前に設定した ID とパスワードを指定します):
2017111703


Node-RED フローエディタ画面が表示されました。IBM Cloud 環境では画面左のパレットリスト内にデータベースや IBM Watson など初めから多くのサービスノードが有効になっているのが便利です(この後実際に使います):
2017111704


なお、Node-RED 環境をローカルで(IBM Cloud を使わずに)用意した場合は別途データベースを用意する必要があります。以下では IBM Cloudant を使う前提で紹介するので、IBM Cloud から Cloudant サービスをインスタンス化してください:
2017112101


【API を作成】
ではここから「普通のウェブアプリケーション」を作っていきます。改めて今回作成するウェブアプリケーションの構成を確認しておきます:
2017111901


今回のアプリケーションでは Node-RED 上に用意する HTML 内のフォームで入力した値を Cloudant に格納し、また格納済みの値を Cloudant から取り出して一覧表示できるようにします。 ということは、今回作成する必要があるのは以下の3つです:
 (1) ユーザー画面の HTML ページ
 (2) (1) 画面で入力した値を Cloudant に保存する API
 (3) (2) で保存した値を Cloudant から一括で取り出す API

この (2) と (3) を最初に作ることにします。

最初に (2) の格納用 API を作ることにします。機能としては((1) で用意する)ユーザー画面で指定した値を受け取ってデータベース(IBM Cloudant)に格納する API です。なので、このような仕様の REST API を作ります:
メソッドパスデータ挙動
POST/myrecordCloudant に保存するデータ(JSON)送信したデータを Cloudant データベースに保存


ではこの API を Node-RED で作ります。Node-RED のキャンバスをクリアします(初期状態で何か配置されているものは全て消します(マウスで選んで DELETE キー))。そして、以下のように3つのノードを配置&接続します:
2017112102


一番左は HTTP Request ノードです(パレット上では "http" と表示されていて、右側だけにジョイントが設定されています)。このノードをダブルクリックして、以下のような属性を指定し、POST /myrecord として動くようにします。最後に「完了」をクリックします:
2017112103


水色のノードは IBM Cloudant out ノードです("cloudant" と表示され、左側にだけジョイントが設置されているものです)。HTTP Request ノードの payload の値を受け取って、そのまま IBM Cloudant に格納する、という目的のノードです。IBM Cloud のボイラープレートから Node-RED 環境を作成した場合はこの部分は自動的にバインド先のサービス名が代入されるはずなので特に変更は不要です。格納先のデータベース名は属性として指定する必要があるので、ダブルクリックしてデータベース名を指定します(以下の例では mydb という名前のデータベースを指定しています)。また msg.payload 内の値だけを格納してほしいので、"Only store msg.payload object?" にチェックを入れておきます:
2017112104


なお、IBM Cloud のボイラープレートを使わずに、IBM Cloudant のインスタンスを別途作った場合は、ここで IBM Cloudant に接続するための username や password も指定する必要があります。


右下にあるのは HTTP Response ノードです。HTTP Request とペアで設定する必要があります。HTTP Request ノードからは IBM Cloudant ノードと HTTP Response ノードに分岐しています。この結果この POST API では HTTP Request ノードに送信されたデータがそのまま HTTP Response として返されると同時に、同じ値が IBM Cloudant の指定データベース(mydb)内に格納される、という挙動になります。ここまでの設定で下のような見た目になります。これだけで (2) のデータ保存用 REST API が出来てしまいました。簡単ですね:
2017112105



では続けて (3) の、データベースから保存されている全レコードを取り出す REST API も追加します。(2) で格納したデータを全て取り出せるように、以下のような API を定義します:
メソッドパスデータ挙動
GET/myrecords(なし)Cloudant データベースから全データを取り出して JSON 配列で返す


先程と同様に、キャンバスに以下のように3つのノードを配置して接続します:
2017112106



一番左は HTTP Request ノードです。このノードをダブルクリックして、以下のような属性を指定し、GET /myrecords として動くようにします:
2017112107


真ん中にあるのは IBM Cloudant in ノードです("cloudant" と表示され、左右両方にジョイントが設置されているものです)。HTTP Request に対して、IBM Cloudant の指定データベースから全レコードを取り出して返すようにするため、ダブルクリックして以下のように属性を指定します(以下の例では mydb という名前のデータベースから全データを取り出す指定をしています):
2017112108


右側にあるのが HTTP Response ノードです。直前の IBM Cloudant in ノードで取得した値を HTTP レスポンスの結果として返します。これで (3) の REST API も完成です:
2017112101


【HTML を作成】
では最後にここまでに作った API を呼び出して使う HTML ページ(利用者向けページ)を作ります。最終的にはそれなりに凝ったものを作りますが、とりあえずは (2) の API の挙動を確認するためのページを作ってみます。キャンバスに以下のように3つのノードを配置して接続します:
2017112102


一番左は HTTP Request ノードです。このノードをダブルクリックして、以下のような属性を指定し、GET /home として動くように(ブラウザから /home にアクセスした時に以下の HTML が表示されるように)します:
2017112103


真ん中は template ノードです。ノードをダブルクリックして以下の HTML 構文を入力します:
2017112104


(テンプレートの入力内容)
<form method="post" action="/myrecord">
Country:<input type="text" name="country" value=""/><br/>
Capital:<input type="text" name="capital" value=""/><br/>
<input type="submit" value="Submit"/>
</form>

↑ country (国名)と capital (首都)を指定して送信ボタンをクリックすると POST /myrecord を呼び出して保存する、という HTML です。


ここまでの作業で3本のフローができました。この状態で右上の「デプロイ」をクリックしてデプロイします:
2017112101


【動作確認】
まずデータを作成する前に IBM Cloud のダッシュボードから、IBM Cloudant のサービスインスタンスを選び、"LAUNCH" ボタンから Cloudant のダッシュボード画面を表示します:
2017112101


IBM Cloudant のダッシュボード画面の左ペインの上から2番目のデータベースアイコンを選択し、現在の Cloudant データベースの一覧を確認します(nodered というデータベースが1つだけ存在していて、この時点では mydb データベースも、その中身のドキュメントも存在していません):
2017112102


ではデータを格納する前に、格納先である mydb データベースを作っておきます。画面右上の "Create Database" をクリックします:
2017112106


データベース作成のダイアログが表示されるので、作成するデータベースの名前(今回の場合は "mydb")を指定して "Create" ボタンをクリックします:
2017112107


すると mydb データベースが作成され、同時に画面は mydb データベースを表示するページに切り替わります(現時点では1つもドキュメントは入っていないので何も表示されません)。元のデータベース一覧に戻るにはデータベース一覧アイコンを再度クリックするか、画面左上の mydb の左にある "<" 印をクリックします:
2017112108


データベース一覧画面に戻りました。今度は nodered データベースに加えて、mydb データベースが追加されていること(そしてドキュメント数がゼロになっていること)が確認できます:
2017112109



改めて先程作成した HTML ページにウェブブラウザでアクセスします。上記では /home というパスに GET リクエストした際に表示される HTML を定義したので、ウェブブラウザで https://(Node-RED のサーバー名)/home にアクセスして、以下のような画面が表示されることを確認します:
2017112103


この Country に国名、Capital にその国の首都を入力して Submit ボタンをクリックします。たとえばこんな感じで:
2017112104


Submit ボタンをクリックすると(実際には POST /myrecord が実行されて)アドレスのパスが /myrecord に変わります。そして画面には POST /myrecord のフローで最後に定義した HTTP Response ノードから結果(送信データを同じもの)が返されます:
2017112105


同時にこのデータは Cloudant out ノードによって Cloudant の mydb データベースに格納されています。Cloudant のデータベース一覧ページをリロードすると、先程まで文書数がゼロだった mydb データベースに1件データが格納されていることが確認できます:
2017112104


具体的なデータの内容を確認するために mydb データベースを選択して中身を見てみましょう。All Documents を Table 状態で確認すると、一件のドキュメントが格納されていて、その capital は Tokyo、country は Japan となっていることが確認できます。期待通りにデータ保存 API が動いていることが確認できました:
2017112102


次にデータ一覧取得 API の挙動を確認します。その前に同様の手順を繰り返して、もう何件かデータを格納しておきます:
2017112103


とりあえず、以下のような5件のデータが入っている状態にしました。このデータを (3) の API で取り出します:
2017112105


ウェブブラウザのアドレス欄に https://(Node-RED のサーバー名)/myrecords と入力してアクセスします。すると (3) で作成した API が実行され、その結果が返ります。以下のように mydb データベース内の5件のドキュメントレコードが JSON 配列になって返されることが確認できます:
2017112106


API が正しく動くことを確認できたので、改めて HTML の見た目を改良します。(1) の(GET /home のフローの) template ノードの属性にて、HTML の内容を以下のように変更して、デプロイします:
<html>
<head>
<title>Countries & Capitals</title>
<script src='//code.jquery.com/jquery-2.2.4.min.js'></script>
<script>
$(function(){
  $('#myform').submit( function(){
    $.ajax({
      type: 'POST',
      url: '/myrecord',
      data: { country: $('#country').val(), capital: $('#capital').val() },
      success: function( data ){
        console.log( data );
        setTimeout( 'list()', 1000 );
      },
      error: function(){
        console.log( 'error' );
      }
    });
    return false;
  });
  list();
});
function list(){
  $('#list').html( '' );
  $.ajax({
    type: 'GET',
    url: '/myrecords',
    success: function( data ){
      if( data && data.length > 0 ){
        for( var i = 0; i < data.length; i ++ ){
          var doc = data[i];
          var tr = '<tr><td>' + doc.country + '</td><td>' + doc.capital + '</td></tr>';
          $('#list').append( tr );
        }
      }
    },
    error: function(){
      console.log( 'error' );
    }
  });
}
</script>
</head>
<body>
  <table border='1'>
  <thead>
  <tr><th>Country</th><th>Capital</th><tr>
  </thead>
  <tbody id='list'>
  </tbody>
</table>
<hr/>

<form method='POST' id='myform'>
  Country: <input type='text' id='country' name='country' value=''/><br/>
  Capital: <input type='text' id='capital' name="capital" value=''/><br/>
  <input type='submit' value='click'/>
</form>
</body>
</html>

具体的には jQuery で AJAX を使ってシングルページの中でデータの追加と一覧表示ができるようにしています。/home にアクセスしてロードすると、まず現在のデータベース内のレコード一覧が表示され、新しいデータを POST すると一覧が同一ページ内で更新されます:
2017112101


※完成形の Node-RED フロー定義をこちらに用意しました。クリップボード経由で読み込むことで再現できると思います:
https://raw.githubusercontent.com/dotnsf/samples/master/nodered_webapp.json




243 ゲームをご存知でしょうか?
2015092901


2048 ゲームは(そこそこ)有名だと思いますが、そのクローンゲームです。2048 ゲームは以下のルールで遊ぶものです:
(1) 矢印キーを使って、画面内のタイルを4方向にスライドさせる
(2) 同じ数字のタイルをぶつけると、数字が倍の1つのタイルができる
(3) ぶつけて出来上がったタイルの数字が得点になる(2048 タイルを作ることが1つの目標)


243 は 2048 と異なり、3つの同じ数字を重ねることで1つにまとまります。2048 よりは難易度高くなっています。具体的なルールは以下になります:
(1) 矢印キーを使って、画面内のタイルを4方向にスライドさせる
(2) 同じ数字のタイルを3つぶつけると、数字が3倍の1つのタイルができる
(3) ぶつけて出来上がったタイルの数字が得点になる(243 タイルを作ることが1つの目標)



で、このゲームの HTML5 が Github 上で公開されていました。これを使って IBM Bluemix 上で 243 ゲーム環境を作ってみます(まあ、特にトリッキーなこともないんですが・・)。

まず Bluemix 上に 243 ゲームが動くランタイムを作成します。サーバーサイドで動くモジュールがないので、Github の staticfile ビルドパックを使っても動くと思いますが、Bluemix ダッシュボードから PHP ランタイムを選んでも(事実上 PHP を使わないので、かなり贅沢な環境ですが)動きます。今回は PHP ランタイムを使う方向でいきましょう:
2015092902


並行して 243 ゲームの zip モジュールを GitHub からダウンロードします:
2015092903


ダウンロードした zip モジュールを展開します。index.html が存在するディレクトリがドキュメントルートになります:
2015092904


ここまでできたら、cf コマンドラインツールを使って、このドキュメントルートを PHP ランタイムにプッシュするだけ、です:
# cd (展開した index.html があるディレクトリ) ドキュメントルートへ移動
# cf login -a https://api.ng.bluemix.net/    cf で Bluemix にログイン(米国DCの場合)
# cf push (PHP ランタイムの名前)        cf で Bluemix に 243 ゲームをプッシュ

プッシュが成功したら、PHP ランタイムの URL にブラウザからアクセスすると 243 ゲームが始まります:
2015092905


ぎゃー、惜しかったけど無理。難しい!!

このページのトップヘ