0

I have an application controller which has 2 methods. In the first method I access database and retrieve a list of drop-down elements for the form. I store the retrieved elements in class level variables. In the second method if there is an error it displays the form as it is else it shows the result. The problem is I class level variables return empty.

    class Application extends Controller {

      var appList = Seq[AppDetail]()
      var countryList = Seq[Country]()

    def home(page:Int,filter: String): Action[AnyContent] = Action.async { implicit request =>

    val futures = for {
            appDetails <- AppDetailsDAO.listAll() //DB retrieve
            countries <- CountriesDAO.listAll()   //DB retrieve
          } yield (appDetails, countries)

          futures.map{case (appDetails, countries) => {
            appList = appDetails
            countryList = countries
            Ok(appList, countryList)
          }

       }

       def result(page:Int, filter: String): Action[AnyContent] = Action.async { implicit request =>

    analyticForm.bindFromRequest.fold(
      formWithErrors => {
        Future.successful(BadRequest(html.home(formWithErrors, appList,  countryList)) //appList, countryList returns empty
      },
      analyticData => {
     Ok(appList, countryList) //appList, countryList returns empty
      }
    }
   }
binshi
  • 1,248
  • 2
  • 17
  • 33

1 Answers1

1

Play is a RESTful framework. This means, among others, you should not share information between two requests. Requests are always stateless.

To solve your problem, you simply fetch appList and countryList in both of your actions. Also remove the two vars in your controller. Whenever possible avoid mutable variables in scala (they can be useful in some cases though). See Daniel C. Sobral's answer on the difference between var and val.

Untested code ahead:

class Application extends Controller {

  def home(page:Int,filter: String): Action[AnyContent] = Action.async { implicit request =>
    fetchDb.map{case (appDetails, countries) => {
      appList = appDetails
      countryList = countries
      Ok(appList, countryList)
    }}
  }

  def result(page:Int, filter: String): Action[AnyContent] = Action.async { implicit request =>
    fetchDb.map{case (appDetails, countries) => {
      analyticForm.bindFromRequest.fold(
        formWithErrors => {
          BadRequest(html.home(formWithErrors, appList,  countryList))
        },
        analyticData => {
          Ok(appList, countryList) //appList, countryList returns empty
        }
      )
    }}
  }

  def fetchDb = {
    val fAppDetails = AppDetailsDAO.listAll()
    val fCountries = CountriesDAO.listAll()

    for {
      appDetails <- fAppDetails
      countries <- fCountries
    } yield (appDetails, countries)
  }
}

Side note: You may noticed that I create the Future instances in fetchDb outside of the for comprehension. This is because a for comprehension is just another representation of a nested flat map. This means countries will not run until appDetails has been completed. The futures will run sequentially. If you put them outside of the for comprehension they will run in parallel.

Community
  • 1
  • 1
Roman
  • 5,651
  • 1
  • 30
  • 41