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!