Aptana JaxerでサーバサイドJavaScriptを始めてみよう!

第4回サンプルアプリ(タスク管理アプリ)作成 -タスクの編集・削除/入力値チェックの共有-

タスクの編集・削除

前回からJaxerのアプリケーション作成に着手し、タスク管理アプリのタスクの登録ができるようになりました。

今回は、タスクの編集・削除と、Jaxerのウリの一つである「サーバサイドとクライアントサイドでの入力値チェックの共有」というところを実装します。

まず、タスクの編集・削除を実際にDBに対して実行する関数を以下のように定義しましょう。 定義する場所は、getAllTask()、add()が定義してあるrunat属性がserver-proxyのscriptタグの中です。

function update(id,text){
    try{
        text = decodeURIComponent(text);
        Jaxer.DB.execute("UPDATE memo set
contents = ? where id = ? ",[text,id]);
        return true;
    }catch(e){
        return false;
    }
}
function del(id){
    try{
        Jaxer.DB.execute("DELETE FROM memo
where id = ? ",id);
        return true;
    }catch(e){
        return false;
    }
}

update(id,text)は、引数で渡したidのタスクの内容をtextに更新し、del(id)は、引数で渡したidのタスクを削除しています。

削除の関数名をdeleteとしていないのは、deleteがjavaScriptの予約語だからです(※今回のサンプルアプリでは、delete文発行のため、終了したタスクはDBから削除する仕様にしていますが、タスクに「未実施」⁠実施済」などのステータスを持たせ、そのステータスを更新する方が自然です⁠⁠。

これらの関数はserver-proxy属性のscriptに書かれていますので、クライアントサイドでも利用することができます。 それでは、タスク名の前の「×」をクリックしたときに、del(id)が実行されるようにしましょう。

×をクリックしたときに、そのタスクのidが分かるように、getAllTask()内の、

 html += '<div><span class="del_memo">×</span><span class="memo" >'+ rs.rowsAsArrays[i][1]+'</span></div>';

の部分を、

html += '<div><span class="del_memo" id="del_'+rs.rowsAsArrays[i][0]+'">×</span><span class="memo" >'+ rs.rowsAsArrays[i][1]+'</span></div>';

に修正します。

×を囲んでいるspanタグをクリックしたときにdel()を実行するため、init()の中に以下のコードを加えます。

$('span.del_memo').click(
    function(){
        del(this.id.split('_')[1]);
        $(this).parent().fadeOut();
    }
);

削除のキーとなるidは、spanタグのid属性の値を"_"で分割して取得するという方法を取りました。 また、del()はDB上のデータを削除するだけですので、画面上からタスクを削除するために、$(this).parent().fadeOut();というコードを書いています。

では、Jaxerを起動し、プレビュータブで削除できるか試してみましょう。

タスクが登録されている以下のような画面で、

01.png

「タスクを追加」の前の「×」をクリックすると、文言がフェードアウトし、以下のように画面から消えます。

02.png

DBから削除されているので、画面をリロードしても、表示されません。

次に、タスクの文字の部分をクリックすると、テキストボックスになって編集可能になり、Enterキーで編集内容が保存されるというコードを書きます。 先ほどと同様に、タスクの内容を囲むspanタグでidを取得できるように、getAllTask()内の、

html += '<div><span class="del_memo" id="del_'+rs.rowsAsArrays[i][0]+'">×</span><span class="memo" >'+ rs.rowsAsArrays[i][1]+'</span></div>';

の部分を、

html += '<div><span class="del_memo" id="del_'+rs.rowsAsArrays[i][0]+'">×</span><span class="memo" id="memo_'+rs.rowsAsArrays[i][0]+'">'+ rs.rowsAsArrays[i][1]+'</span></div>';

に修正します。

また、タスクの内容をクリックしたときの動作を、init()の中に以下のように定義します。

$('span.memo').click(
    function(){
       if($('form',this).length > 0) return;
        var id = this.id.split('_')[1];
        var rewrite = '<form name="rewriteForm" id="rwForm"><input name="content" type="text" value="'+$(this).text()+'"></form>';
        $(this).html(rewrite);
        $('#rwForm').submit(
            function(){
                update(id,encodeURIComponent($(':text',this).val()));
                $(this).parent().text($(':text',this).val());
                return false;
            }
        );
    }
);

if($('form',this).length > 0) return;の部分で、一度クリックして入力フォーム状態になっていたとき、これ以降のコードが実行されないようにしています。 spanタグをクリックすると、spanタグで囲まれたテキストをテキストボックスの値にセットし、入力フォームを生成します。

生成した入力フォームのsubmitイベントで、update(id,text)を実行し、入力フォームを消して元のspanタグの状態に戻すという処理です。

それでは、再度プレビュータブで動作を確認しましょう。 以下のような画面で、⁠原稿を書く」をクリックします。

03.png

すると、以下のようにテキストボックスが表示されます。

04.png

内容を編集し、Enterキーを押下します。

05.png

以下のように編集内容が反映されます。

06.png

DBもしっかり更新されていることを確認するために、ページをリロードして確認しましょう。

入力値チェックの共有

タスクの登録・編集・削除ができるようになったところで、Jaxerのウリの1つとなっているValidateコードのサーバサイドとクライアントサイドでの共有というところを実装してみます。

今回、登録するタスクは、文字列の長さが1以上100以下でなければエラーとする仕様にします。 その場合のValidateコードは以下のようになります。

function checkTaskValue(text){
    if(text && text.length <= 100){
        return true;
    }
    return false;
}

関数の引数として渡された文字列の長さをチェックして、1~100文字であればtrue、それ以外でfalseを返す仕様になっています。 この関数はサーバサイドとクライアントサイドの両方で利用するので、runat="both"のscriptタグ中に記述します。

定義した後、クライアントサイドの登録と更新処理の前、およびサーバサイドのINSERTとUPDATE処理の前で呼び出して、入力値をチェックしましょう。

本サンプルアプリでは、概要解説のため簡易な実装内容になっていますが、文字種のチェックや必須チェックなどのチェックの実装も同じように行えます。

これまでの内容を反映すると、以下のようになります。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <script runat="server">
            (function(){
                if (Jaxer.application.get("isInitialized")) return;
                var sql = "CREATE TABLE IF NOT EXISTS memo " +
                    "( id INTEGER PRIMARY KEY AUTO_INCREMENT" +
                    ", contents TEXT NOT NULL" +
                    ")";
                Jaxer.DB.execute(sql);
                Jaxer.application.set("isInitialized", true);
            })();
        </script>
        <script src="lib/jquery/jquery.js" runat="both"></script>
        <script runat="both">
            function loadAllTask(){
                var html = getAllTask();
                $('#tasks').html(html);
            }
            function checkTaskValue(text){
                if(text && text.length <= 100){
                    return true;
                }
                return false;
            }
        </script>
        <script runat="server-proxy">
            function getAllTask(){
                var rs = Jaxer.DB.execute('SELECT * FROM memo');
                var html = '';
                for(var i = 0,len = rs.rowsAsArrays.length;i<len;i++){
                    html += '<div><span class="del_memo" id="del_'+rs.rowsAsArrays[i][0]+'">×</span><span class="memo" id="memo_'+rs.rowsAsArrays[i][0]+'">'+ rs.rowsAsArrays[i][1]+'</span></div>';
                }
                return html;
            }
            function add(task){
                if(checkTaskValue(task)==false)return false;
                try{
                    task = decodeURIComponent(task);
                    Jaxer.DB.execute("INSERT INTO memo (contents) VALUES (?)",task);
                    return true;
                }catch(e){
                    return false;   
                }
            }
            function update(id,text){
                if(checkTaskValue(task)==false)return false;
                try{
                    text = decodeURIComponent(text);
                    Jaxer.DB.execute("UPDATE memo set contents = ? where id = ? ",[text,id]);
                    return true;
                }catch(e){
                    return false;
                }
            }
            function del(id){
                try{
                    Jaxer.DB.execute("DELETE FROM memo where id = ? ",id);
                    return true;
                }catch(e){
                    return false;
                }
            }
        </script>
        <script runat="client">
            $(init);
            function init(){
                $("#addForm").submit(function(){
                        if(checkTaskValue($('#newTask').val())==false)return false;
                        add(encodeURIComponent($('#newTask').val()));
                        $('#newTask').val('');
                        loadAllTask();
                        init();
                        return false;
                });
                $('span.del_memo').click(
                    function(){
                        del(this.id.split('_')[1]);
                        $(this).parent().fadeOut();
                    }
                );
                $('span.memo').click(
                    function(){
                       if($('form',this).length > 0) return;
                        var id = this.id.split('_')[1];
                        var rewrite = '<form name="rewriteForm" id="rwForm"><input name="content" type="text" value="'+$(this).text()+'"></form>';
                        $(this).html(rewrite);
                        $('#rwForm').submit(
                            function(){
                                if(checkTaskValue($('#newTask').val())==false)return false;
                                update(id,encodeURIComponent($(':text',this).val()));
                                $(this).parent().text($(':text',this).val());
                                return false;
                            }
                        );
                    }
                );
            }
        </script>
        <title>タスク管理</title>
        <style type="text/css">
            body {background-color:#FFFFE0}
            form {display:inline;margin:0px;}
            span.del_memo {cursor:pointer;font-weight:bold;}
        </style>
    </head>
    <body onserverload="loadAllTask();">
        <form name="add_form" id="addForm">
            <div><input type="text" size="50" name="new_task" id="newTask"/></div>
        </form>
        <div id="tasks">
            <div><span class="del_memo">×</span><span class="memo" >牛乳を買う</span></div>
            <div><span class="del_memo">×</span><span class="memo" >
                <form name="edit_form"><input name="edit_task" type="text" value="サンプルアプリを作る"/></form>
            </span></div>
        </div>
    </body>
</html>

これでタスク管理アプリが完成しました。

Jaxerへのデプロイ

では、作成したアプリを、Jaxerサーバへデプロイしましょう。

まず、Aptana Studio上で、プロジェクトのフォルダを右クリックし、⁠Export」を選択します。

07.png

表示されたダイアログで、⁠General⁠⁠→⁠File System」を選択します。

08.png

次に表示された画面で、今回のtaskフォルダを選択します。

フォルダ直下の.projectファイルは、Aptana Studio用のファイルなのでチェックを外します。 To directoryに出力先のフォルダを選択します。今回はCドライブ直下のtmpフォルダにしています。 Optionの「Create directory structure for files」を選択します。

この状態でFinishをクリックします。

09.png

すると、Cドライブのtmpフォルダにファイルが出力されます。

10.png

このtaskフォルダを、Jaxerをインストールしたフォルダのpublicフォルダへ移動します。移動後、JaxerのインストールフォルダのStartServers.batを実行してサーバを起動しましょう。 すでにJaxerサーバが起動していた場合は再起動の必要はありません。

サーバが起動したら、以下のアドレスにアクセスしてみましょう。

http://localhost:8081/task/

以下のように、初期状態のタスク管理アプリが表示されます。

11.png

アプリ作成時に入力したデータは移行されません。

Jaxerのサンプルアプリの作成、およびデプロイの解説は以上です。

次回は、JaxerアプリケーションをAmazon Web ServiceのEC2で利用する方法を解説します。

おすすめ記事

記事・ニュース一覧