Lesson 9: Passing Parameters to Scala Play Controllers

Typically in an application, we display a list of objects and the user can click on one and see it in detail. To do this we pass an id of some sort to the Controller in a normal link, not a form. With Play, and its emphasis on compile-time safety, this link can be generated and typesafe. Let’s look at how by querying a table and then showing detail for any item we click on.

Step 1: Generate the Skeleton Application.
I outline that here: https://scalaplayschool.wordpress.com/2014/08/10/hello-world-using-scala-play/

I download activator (that runs the Play framework), unzip it, put it on my Path and use these commands:

$ cd
$ activator new lesson9
( at this point I choose option 4 – play-scala)
$ cd lesson9
$ activator eclipse
$ activator run

Step 2: Configure the Application for MySQL
Same as Lesson 8. Append this to build.sbt:

libraryDependencies += "mysql" % "mysql-connector-java" % "5.1.27"

This needs to go in application.conf file. I add these just below the commented-out versions:

db.default.driver=com.mysql.jdbc.Driver
db.default.url="jdbc:mysql://ensembldb.ensembl.org/information_schema"
db.default.user=anonymous
db.default.password=""

Step 3: Model

To keep the controller simple I’m putting the database code into the companion object of each case class. This is app/models/CharacterSet.scala:

package models

import scala.collection.mutable.MutableList

case class CharacterSet(key: String, description: String)

object CharacterSet {

  import play.api.db._
  import play.api.Play.current

  def list = {
    val list = MutableList[CharacterSet]()
    DB.withConnection { conn =>
      val stm = conn.createStatement()
      val res = stm.executeQuery("""
      select character_set_name, description 
      from  character_sets 
      order by character_set_name """)
      while (res.next()) {
        list.+=(CharacterSet(res.getString(1), res.getString(2)))
      }
    }
    list
  }
}

This is app/models/CharacterSetDetail.scala:

package models

case class CharacterSetDetail(name: String, defaultCollateName: String, description: String, maxLength: Int)

object CharacterSetDetail {
  
  import play.api.db._
  import play.api.Play.current

  def get(code: String): Option[CharacterSetDetail] = {
    DB.withConnection { conn =>
      val stm = conn.createStatement()
      val ps = conn.prepareStatement(detailQuery)
      ps.setString(1, code);
      val res = ps.executeQuery()
      while (res.next()) {
        return Some(CharacterSetDetail(res.getString(1), res.getString(2),
          res.getString(3), res.getInt(4)))
      }
    }
    return None
  }

  val detailQuery = """
        select 
          character_set_name, default_collate_name,
          description, maxlen 
        from character_sets 
        where character_set_name = ?"""
}

Step 4: View
Update app/views/index.scala.html to show our table of CharacterSets with links to view the details of any of them:

@(list:Seq[CharacterSet])

@main("Welcome to Play") {
	<h1>Table of Character Sets</h1>
	<table>
    @for(t <- list) {
    	<tr>
    		<td><a href="@routes.Application.detail(t.key)">@t.key</a></td>
    		<td>@t.description</td>
    	</tr>	
    }
	</table>
}

This is the detail view in app/views/detail.scala.html:

@(characterSet:CharacterSetDetail)

@main("") {
	<h1>Detail for Character Set @characterSet.name</h1>
	<div> Name: @characterSet.name </div>
	<div> Description: @characterSet.description </div>
	<div> Default Collate Name: @characterSet.defaultCollateName </div>
	<div> Max Length: @characterSet.maxLength </div>
	<div>
		<a href="@routes.Application.index()">Back to list</a>
	</div>
}	

Step 5: Controller

We need to update app/controllers/Application.scala:

package controllers

import play.api._
import play.api.mvc._
import play.api.Play.current
import scala.collection.mutable.MutableList
import models.CharacterSet
import models.CharacterSetDetail

object Application extends Controller {

  import play.api.db._

  def index = Action {
    val list = CharacterSet.list
    Ok(views.html.index(list))
  }

  def detail(code: String) = Action {
    val detailObject = CharacterSetDetail.get(code)
    detailObject match {
      case Some(characterSet) => Ok(views.html.detail(characterSet))
      case None => NotFound("No such Character Set: " + code)
    }
  }

}

Step 6: Routes
We just need to add a route entry for the detail view – this is the link that takes a String parameter – the name of the CharacterSet. This is conf/routes :

GET  /              controllers.Application.index
GET  /detail/:name  controllers.Application.detail(name:String)
GET  /assets/*file  controllers.Assets.at(path="/public", file)

This should give us the list page at http://localhost:9000/ as shown:

Clicking on a link will bring up the detail page:

And that’s it. I think it’s easy to see the power of Play generating links.

Advertisements