LoginSignup
4
4

More than 5 years have passed since last update.

Playframework付属のサンプル'websocket-chat'を見てみる(2/4)

Last updated at Posted at 2014-08-07

前回の続き。今回はクライアント側のWebSocketの記述を中心に見ています。

chatRoom.scala.html

chatRoom.scala.html
@(username: String)

@main(username) {

    <div class="page-header">
        <h1>Welcome to the chat room 
            <small>You are chatting as @username</small></h1>
    </div>

    <div id="onError" class="alert-message error">
        <p>
            <strong>Oops!</strong> <span></span>
        </p>
    </div>

    <div id="onChat" class="row">
        <div class="span10" id="main">
            <div id="messages">
            </div>
            <textarea id="talk"></textarea>
        </div>
        <div class="span4">
            <h2>Members</h2>
            <ul id="members">
            </ul>
        </div>
    </div>

    <script type="text/javascript" charset="utf-8"
        src="@routes.Application.chatRoomJs(username)"></script>

}

mainのViewにはめ込んで表示している。
#onErrordivにエラー表示、#onChatdivの左の列にチャットの内容、右の列にルーム内の人一覧(Members)を表示している。一番下でjavascriptを読み込んでいるが、指定の仕方が独特である。routesファイルを見てみると以下のようになっている。

routes
GET    /assets/javascripts/chatroom.js
    controllers.Application.chatRoomJs(username)

JSのsrc指定の部分は、実行時には

src="/assets/javascripts/chatroom.js?username=a"

と変換されるため、クエリ文字列を渡しながらJSを取得しにいくことになる。routesファイルの定義により、/assets/javascripts/chatroom.jsにアクセスした場合はApplication.javachatRoomJsアクションが呼ばれることになっている。ここでJSを生成し、ブラウザ側に返している。

Application.java
public static Result chatRoomJs(String username) {
    return ok(views.js.chatRoom.render(username));
}

ちなみにviewsパッケージ内にchatRoom.scala.jsという名前で配置したJSのテンプレートファイルは、コンパイルされるとviews.jsパッケージに配置され、views.js.chatRoomという名前でアクセスできるようになる模様。

chatRoom.scala.js

chatRoom.scala.js
@(username: String)

$(function() {
    var WS = window['MozWebSocket'] ? MozWebSocket : WebSocket
    var chatSocket = 
        new WS("@routes.Application.chat(username).webSocketURL(request)")

    var sendMessage = function() {
        chatSocket.send(JSON.stringify(
            {text: $("#talk").val()}
        ))
        $("#talk").val('')
    }

    var receiveEvent = function(event) {
        var data = JSON.parse(event.data)

        // Handle errors
        if(data.error) {
            chatSocket.close()
            $("#onError span").text(data.error)
            $("#onError").show()
            return
        } else {
            $("#onChat").show()
        }

        // Create the message element
        var el = $('<div class="message"><span></span><p></p></div>')
        $("span", el).text(data.user)
        $("p", el).text(data.message)
        $(el).addClass(data.kind)
        if(data.user == '@username') $(el).addClass('me')
        $('#messages').append(el)

        // Update the members list
        $("#members").html('')
        $(data.members).each(function() {
            var li = document.createElement('li');
            li.textContent = this;
            $("#members").append(li);
        })
    }

    var handleReturnKey = function(e) {
        if(e.charCode == 13 || e.keyCode == 13) {
            e.preventDefault()
            sendMessage()
        }
    }

    $("#talk").keypress(handleReturnKey)

    chatSocket.onmessage = receiveEvent

})

ここがクライアント側のWebSocketを担っている要となっている模様。

MozWebSocketが定義してある場合はそっちを使うようFireFox対応がしてあるが、FF11からはWebSocketに統一された模様。

var chatSocket = 
    new WS("@routes.Application.chat(username).webSocketURL(request)")

という記述をしておくと、実行時に

var chatSocket = new WS("ws:\/\/localhost:9000\/room\/chat?username=a")

というWebSocket用プロトコルのURLに変換してくれる。ここではそのURLを用いてWebSocketオブジェクトを作成している。同時に、非同期で、サーバとのWebSocketコネクションの確立も行われているようである(The WebSocket API (日本語訳))。

routesファイルを見てみると以下のようになっており、この時Application.javachatメソッドが呼ばれるのがわかる。

routes
GET    /room/chat    controllers.Application.chat(username)

この時、サーバ側でもWebSocketオブジェクトを作成しブラウザ側に返している。これでサーバとのWebSocketコネクションが確立する。

Application.java
public static WebSocket<JsonNode> chat(final String username) {
    return new WebSocket<JsonNode>() {

        // Called when the Websocket Handshake is done.
        public void onReady(WebSocket.In<JsonNode> in, WebSocket.Out<JsonNode> out){

            // Join the chat room.
            try { 
                ChatRoom.join(username, in, out);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    };
}

chatRoom.scala.jsではWebSocketコネクションの確立の他、

  • sendMessage
  • receiveEvent
  • handleReturnKey

の3つの関数を定義している。

sendMessageは、WebSocketへのメッセージ送信を行っており、テキストエリアの文字列をJSON化した後、WebSocketの.send()メソッドでサーバに送信している。送信が終わったらテキストエリアをクリアしている。

receiveEventは、chatSocket.onmessageと紐付けられており、WebSocketでメッセージを受けとった際に行う処理が書いてある。
onmessageの引数にはWebSocketの戻り値としてJSONオブジェクトが帰ってくるので、まずそれをパースしている。JSONにエラーが入っていた場合はエラーを表示する。
エラーがない場合は、メッセージ用のdivを追加してチャットメッセージ用のdivの一番下に追加、ルームメンバー一覧もulを毎回クリアして新たにliを組み直している。自分の発言にはそれとわかるようCSSのクラスを追加したりしている。

handleReturnKeyは、テキストエリアでのEnterキーの挙動を定義している。


長くなってきたのでここまで。次回はサーバ側の挙動について見ていく。


Playframework付属のサンプル'websocket-chat'を見てみる(1/4)
Playframework付属のサンプル'websocket-chat'を見てみる(3/4)
Playframework付属のサンプル'websocket-chat'を見てみる(4/4)

4
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
4
4