Table of Contents

Forms 🔗

Form data can be extracted with Request.current.bodyForm[MyData].
The MyData needs to have a FormDataRW given instance.

Create a file form_handling.sc and paste this code into it:

            //> using scala "3.7.0"
//> using dep ba.sake::sharaf-undertow:0.13.0

import ba.sake.formson.FormDataRW
import ba.sake.sharaf.{*, given}
import ba.sake.sharaf.undertow.UndertowSharafServer

val routes = Routes:
  case GET -> Path() =>
    Response.withBody(ContactUsView)
  case POST -> Path("handle-form") =>
    case class ContactUsForm(fullName: String, email: String) derives FormDataRW
    val formData = Request.current.bodyForm[ContactUsForm]
    Response.withBody(s"Got form data: ${formData}")

UndertowSharafServer("localhost", 8181, routes).start()

println("Server started at http://localhost:8181")

def ContactUsView =
  html"""
    <!DOCTYPE html>
    <html>
    <body>
        <form action="/handle-form" method="POST">
            <div>
                <label>Full Name: <input type="text" name="fullName" autofocus></label>
            </div>
            <div>
                <label>Email: <input type="email" name="email"></label>
            </div>
            <input type="submit" value="Submit">
        </form>
    </body>
    </html>
  """


          

Then run it like this:

            scala form_handling.sc 

          

Now go to http://localhost:8181 and fill in the page with some data.

When you click the "Submit" button you will see a response like this:

            Got form data: ContactUsForm(Bob,bob@example.com)

          

Named Tuples 🔗

You can also use named tuples to parse form params: Request.current.bodyForm[(fullName: String, email: String)]. In this case you don't even need to define a separate class!

Note that you can't use Validation with named tuples

Union Types 🔗

Union Types are also handy sometimes. Say you have a filter functionality, where a user can submit a "firstName" or "lastName". You can write: Request.current.bodyForm[(firstName: String) | (lastName: String)].

Here we are combining 2 named tuples together with into a union type. You could use any other type of course.


You can also write Request.current.bodyForm[(id: Int | String)], but we would not recommend it.
Of course, if you need it, go for it!

In this case, it will first try to parse an Int and if that fails it will parse a String.