Scala/Twirl variables and scope/defining

1.3k views Asked by At

I'm a front ender by trade but I've been asked to hop on to a Scala project which leverages the Play framework so includes Twirl. I'm trying to create a template which displays slightly different outputs depending on the string which is passed in.

Here is my code:

@(status: String)(implicit messages: Messages)

@{
    if(status == "00") {
        val pageTitle = "Page title"
        val appStatus = "className"
        val title = "message"
        val subTitle = "message"
        val step1Status = "className"
        val step2Status = "className"
        val step3Status = "className"
        val image1 = "/customs/assets/images/image.png"
        val image2 = "/customs/assets/images/image.png"
        val image3 = "/customs/assets/images/image.png"
        val optionalHeading = ""
        val optionalParagraph = ""
        val listContents = "<li>@Messages('message')</li><li>@Messages('message')</li>"
        val optionalLink = "<br /><a class='button' href='@routes.DashboardController.display(custom)' role='button'>@Messages('message')</a>"
    }

    if(status == "01") {
        //Other variables
    }
    if(status == "04") {
        //Etc...
    }
}

@layout(${pageTitle}) {
        <div class="content__body">
            <div class="hero">
                <div class="${appStatus}">
                    <h1 class="bold-large">@Messages(${title})</h1>
                    <p>
                        ${afterSubTitle}
                    </p>
                    <ol class="appstatus-steps">
                        <li><span class="${step1Status}"><img alt="Complete" title="Complete" src=" + ${image1} + ">@Messages("messages.Received")</span></li>
                        <li><span class="${step2Status}"><img alt="Complete" title="Complete" src=" + ${image2} + ">@Messages("messages.Processing")</span></li>
                        <li><span class="${step3Status}"><img alt="Complete" title="Complete" src=" + ${image3} + ">@Messages("messages.Decision")</span></li>
                    </ol>
                </div>
            </div>

            ${optionalHeading}
            ${optionalParagraph}

            <h2>@Messages("messages.next")</h2>

            <ul class="list list-bullet">
                ${listContents}
            </ul>

            ${optionalLink}
        </div>
    }

So, as you can see, the idea is that this page is called with a code (status) and based on that string, a number of variables are defined which alter the way the page is formed; content/css classes/images etc.

This doesn't work (you may be surprised to learn!)

It's really unclear to me WHY it doesn't though. I thought that you accessed variables using a ${Variable} format, but it's possible that you're supposed to use the @Variable format instead.

Even when I try to change them to the @Variable way, I'm still getting problems and I loosely understand that this is to do with scope in Scala/Twirl and there's a "defining" keyword which can be used. I've read some documentation but I'm not getting it.

Could anyone shed some light on this?

EDIT

Okay, let's just consider the below code instead:

@(status: String)(implicit messages: Messages)

@{
    if(status == "00") {
        val myClass = "custom-class-1"
    }
    if (status == "01") {
        val myClass = "custom-class-2"
    }
@layout("page title") {
    <div class="@myClass">This is a div</div>
}

So 'all I want to do' :) is be able to define a variety of variables based on the status and then use those variables in the template of the page.

Does that make sense? I'm so new to Scala so Tuples/Defining is lost on me at the moment, but I am learning!

2

There are 2 answers

2
Sternjobname On BEST ANSWER

The answer by @pedrorijio is almost exactly correct, but the actual code which worked for me in the end was this:

@myClass = @{status match {
  case "00" => "custom-class-1"
  case "01" => "custom-class-2"
}}

Thank you for the assistance!

6
pedrorijo91 On

You seem to be more used to javascript variables and scopes :)

In Scala and Twirl, if you only define a variable (in this case it's a value because it's immutable) inside an if statement, the value is only accessible inside the if block

One solution could be to have the if statement to return a tuple with all the variables, and use the power of assignment operation, like:

val (myA, myB, myC) = if(something) {
 val a = "a"
 val b = "b"
 val c = "c"

 (a,b,c) // this will return the 3 variables/values because if statements are an expression, and expressions return something
} else {
 val a = "A"
 val b = "B"
 val c = "C"

 (a,b,c)
}

// now you have a,b,c defined in this scope and you can do whatever you need with them

if you want to return different variables depending on the if statement, you will have more complications. a solution would be to make the not needed variables null or an Option

Let me know if you need anything else better explained :)

EDIT: solution for minified edited example

I don't have anything here to check if it compiles, but it should be something like:

@(status: String)(implicit messages: Messages)

@{
    val myClass = if(status == "00") {
        "custom-class-1"
    } else if (status == "01") {
        "custom-class-2"
    } else {
      ""
    }

@layout("page title") {
    <div class="@myClass">This is a div</div>
}

Alternative using pattern matching:

replace the if/else statement with:

val myClass = status match {
          case "00" => "custom-class-1"
          case "01" => "custom-class-2"
          case _ => ""
        }

(the case _ is like "in every other case")