ブラウザからのステータス更新処理を実装しました
- Play Frameworkを用いた Todo 管理アプリの実装を進め、サーバーの更新処理を実装しました
- あちこち触ることになり記事の内容が若干散漫ですが、ご容赦ください
- やったこと
jQuery でのサーバー呼び出し
$.get
を利用する、以下のメソッドを追加します。
function reqUpdateTodoStatus(status){ $.get('/todo/update', { ids: getSelectedIds, status: status }, function(){location.reload()} ) }
第1引数はリクエスト先のURL, 第2引数はリクエストのパラメータ、第3引数はコールバック処理です。
コールバック処理で画面の更新(location.reload()
)を行うことで、即座に画面に変更内容が反映されます。
更新メソッドのルーティングを追加
config/routes に以下のrouteを追加
引数の型がStringだったら、型は省略できます。
GET /todo/update controllers.TodoController.update(ids, status)
Controller
def update(ids: String, status: String) = Action { todoManager.update(ids.split(",").map(_.toInt).toList, status) Ok(todoView(todoManager.list)) }
困っているところ
自分としては、Int、百歩譲ってStringの配列で引数を回したかったんですが、どうも"10001, 10002"のようなカンマ区切りのStringで来てしまいます。
ココらへんの記事を参考にして色々とやってみたのですが上手く行かず,,,
リクエストの投げ方が悪いのか、受け取り方が悪いのか、もうちょっと試行錯誤したいと思います。
Repository クラス分離、ファイルの書き込み処理を実装
Service クラスと Repository クラスを分離し、Repository クラスにファイルの書き込み処理を実装しました。
Service
package services import javax.inject.Inject import com.google.inject.Singleton import services.domain.TodoStatus import services.repository.TodoRepository /** * service class for todo management */ @Singleton class TodoManager @Inject()(todoRepository: TodoRepository) { /** * get Todo List * * @return todo-list order by id */ def list = todoRepository.getTodoList() /** * update todo status * * @param ids id list of target TODO * @param status TodoStatus change to */ def update(ids: List[Int], status: String): Unit = todoRepository.updateTodoList(ids, TodoStatus.withName(status)) }
かなりスッキリしました。基本的に、Inject している repository のメソッドをコールするだけになっています。
Repository
package services.repository import java.io.PrintWriter import com.google.inject.Singleton import play.Environment import services.domain.{Todo, TodoStatus} import scala.collection.mutable import scala.io.Source /** * repository class for todo management * */ @Singleton class TodoRepository() { private val filePath = "data/TODO.txt" private val encoding = "UTF-8" def getTodoList(): List[Todo] = { sortById(readTodoFile()) } def updateTodoList(ids: List[Int], status: TodoStatus) = { val modified = readTodoFile() ids.foreach(id => { val todo: Todo = modified.get(id).get todo.update(status) modified.put(todo.getId, todo) }) writeTodoFile(sortById(modified)) } private def readTodoFile(): mutable.Map[Int, Todo] = { val map: mutable.Map[Int, Todo] = new mutable.HashMap[Int, Todo] val s = Source.fromFile(Environment.simple().getFile(filePath), encoding) val strList: List[String] = try s.getLines.toList finally s.close() strList.foreach(str => { val data: Array[String] = str split "\t" val todo: Todo = new Todo(data(0).toInt, TodoStatus.withName(data(1)), data(2)) map.put(todo.getId, todo) }) return map } private def writeTodoFile(list: List[Todo]) = { val pw = new PrintWriter(filePath, encoding) try list.foreach(todo => pw.write(todo.toTsvString + "\n")) finally pw.close() } private def sortById(map: mutable.Map[Int, Todo]): List[Todo] = { map.values.toList.sortBy(_.getId) } }
private def writeTodoFile(list: List[Todo]) = { val pw = new PrintWriter(filePath, encoding) try list.foreach(todo => pw.write(todo.toTsvString + "\n")) finally pw.close() }
- workspace の中のファイルを読み込むために、Environment.getFile()を利用しています。
Environment.simple()
でルートからの相対パスを指定してファイルを選択できます。
val s = Source.fromFile(Environment.simple().getFile("data/TODO.txt"), encoding)
Enum の書きっぷりを変更
Enumeration
クラスを使うよりは sealed trait
を使う方が柔軟に行けそうだったものの、JavaでいうvalueOf
が無いのは辛かったので object の中に case object
を列挙し、withName
を実装しました。
package services.domain sealed trait TodoStatus /** * todo status */ object TodoStatus { case object UNDONE extends TodoStatus case object DOING extends TodoStatus case object DONE extends TodoStatus def withName(s: String) = s.toLowerCase match { case "undone" => UNDONE case "doing" => DOING case "done" => DONE } }
動作確認
“UNDONE"な2つを"DOING"に。
感想と、次にやること
- パラメータの型にはハマったものの、サクッとブラウザからサーバーへのメソッドが通ったのは嬉しかったです。
- パラメータの型については、もう少しきれいなやり方がないか頑張りたい。
- いっそ、ReqMessageクラスを作って、Jsonで展開するほうが楽かもしれない。
- Enumだったり、ファイルの書き込みだったり、サーバー側のScalaの知識もついたなぁと。
- updateが出来たので、add/delete も実装したい
- データの管理をDBにしたい、Scala書いてる方が楽しいけど、ちゃんと勉強しなければ。
以上です。今週は色々と触れて勉強になりました。