Use Scala match expression in Play Scala template

TIL how to use Scala match expression in a Play Scala template.

Use case example

We have a table in a Play Scala template, with a column named Status. Let's say that status is a payment status. In this column we want to show the statuses in a Bootstrap badge. These are the statuses and their badge color:

Authorised Pending Confirmed Paid out Failed Cancelled Expired Charged back Open

All other statuses must be badge-light.

In the for loop we use an if-statement for each status to use the appropriate badge color.

@for(subscription <- subscriptionList) {

    @if(subscription.getStatus.equals("Authorised")) {
        <span class="badge badge-primary">Authorised</span>
    }

    @if(subscription.getStatus.equals("Pending")) {
        <span class="badge badge-secondary">Pending</span>
    }

    @* etcetera *@
}

Because some statuses have the same badge colour we can shorten the code by using an OR in the if:

@if(subscription.getStatus.equals("Confirmed") || subscription.getStatus.equals("Paid out")) {
    <span class="badge badge-success">@subscription.getStatus</span>
}

I do not think the view looks clean with so many if statements. Let us handle it the Scala way.

How to handle this use case the Scala way?

In Java we can, and probably would, use a switch statement for this use case. Unfortunately, we can not use Java in Play Scala templates. However, we can use Scala. In Scala you can use the match expression like the switch statement in Java.

We will declare a reusable code block just under the template parameters and name it badgeColour. badgeColour accepts a String, this will be the status. Inside badgeColour we will create the match expression:

@(subscriptionList: List[Subscription])

@badgeColour(subscription: String) = {
    @subscription match {
        case "Authorised" => { primary }
        case "Pending" => { secondary }
        case "Confirmed" => { success }
        case "Paid out" => { success }
        case "Failed" => { danger }
        case "Cancelled" => { danger }
        case "Expired" => { warning }
        case "Charged back" => { info }
        case "Open" => { light }
    }
}

The for loop will look this:

@for(subscription <- subscriptionList) {
    <span class="badge badge-@badgeColour(subscription.getStatus)">@subscription.getStatus</span>
}

The code already works now, but it still is not DRY. We can optimise it further.

Notice that we use some badge colours more than once. We can place the statuses that have the same badge colour in the same case, just separated with a regex (|):

@badgeColour(subscription: String) = {
    @subscription match {
        case "Authorised" => { primary }
        case "Pending" => { secondary }
        case "Confirmed" | "Paid out" => { success }
        case "Failed" | "Cancelled" => { danger }
        case "Expired" => { warning }
        case "Charged back" => { info }
        case "Open" => { light }
    }
}

Now, do not forget the requirement that all other statuses must be badge-light. We can add a default case to the match expression:

case _ => { light }

If we really want to be DRY, and ofcourse we want to, we can also remove the status Open as a case, because it is not one of the other cases, so it will be the default light.

@badgeColour(subscription: String) = {
    @subscription match {
        case "Authorised" => { primary }
        case "Pending" => { secondary }
        case "Confirmed" | "Paid out" => { success }
        case "Failed" | "Cancelled" => { danger }
        case "Expired" => { warning }
        case "Charged back" => { info }
        case _ => { light }
    }
}

We want to add a comment to let other developers in our team know that we have removed it intentionally. We can add this comment only inside the curly brackets ({ }) or outside of the match expression.

Finally, the match expression will look like this:

@badgeColour(subscription: String) = {
    @subscription match {
        case "Authorised" => { primary }
        case "Pending" => { secondary }
        case "Confirmed" | "Paid out" => { success }
        case "Failed" | "Cancelled" => { danger }
        case "Expired" => { warning }
        case "Charged back" => { info }
        case _ => { @* applies also if "Open" *@ light }
    }
}

WDYT?

Let me know what you think of the way I handled this use case!


SHARE THIS ARTICLE