@@ -176,7 +190,14 @@
>Go Back
+
Next StepGo Back
+
Confirm this.copy(
+ step1 = this.step1.fromFormData(rawData),
+ currentStep = stepNum + 1
+ )
+ case 2 => this.copy(
+ step2 = this.step2.fromFormData(rawData),
+ currentStep = stepNum + 1
+ )
+ case 3 => this.copy(
+ step3 = this.step3.fromFormData(rawData),
+ currentStep = stepNum + 1
+ )
+ case 4 => this.copy(
+ currentStep = stepNum + 1
+ )
+ case _ => this
+ }
+ }
+ }
+
+ enum PlanType:
+ case Arcade, Advanced, Pro
+
+ enum Addons:
+ case OnlineService, LargerStorage, CustomProfile
+
+ sealed trait StepAnswers {
+ def fromFormData(rawData: String): StepAnswers
+ }
+ object StepAnswers {
+ final case class Step1(
+ name: String = "",
+ email: String = "",
+ phone: String = ""
+ ) extends StepAnswers {
+ override def fromFormData(rawData: String): Step1 = {
+ val fieldValues = rawData.split("&").map { field =>
+ val Array(name, value) = field.split("=")
+ name -> value
+ }.toMap
+
+ val name = fieldValues.getOrElse("name", "")
+ val email = fieldValues.getOrElse("email", "")
+ val phone = fieldValues.getOrElse("phone", "")
+
+ Step1(name, email, phone)
+ }
+ }
+ final case class Step2(
+ planType: PlanType = PlanType.Arcade,
+ isYearly: Boolean = false
+ ) extends StepAnswers {
+ override def fromFormData(rawData: String): Step2 = {
+ val a = 1
+ Step2()
+ }
+ }
+ final case class Step3(addons: Set[Addons] = Set.empty)
+ extends StepAnswers {
+ override def fromFormData(rawData: String): Step3 = {
+ val a = 1
+ Step3()
+ }
+ }
}
}
diff --git a/15-multi-step-form/src/main/scala/multistepform/Routes.scala b/15-multi-step-form/src/main/scala/multistepform/Routes.scala
index be6143f..61d7f33 100644
--- a/15-multi-step-form/src/main/scala/multistepform/Routes.scala
+++ b/15-multi-step-form/src/main/scala/multistepform/Routes.scala
@@ -19,16 +19,23 @@ case class Routes()(implicit cc: castor.Context, log: cask.Logger)
templateEngine.setTemplateResolver(templateResolver)
val sessoinCookieName = "sessionId"
- /**
- * This route works with and without 'sessionId' cookie present
- * set's this cookie if not present, and returns initial 'index.html'
- * where the form is not yet initialized, and will be requested for the session
- */
+
+ /** This route works with and without 'sessionId' cookie present set's this
+ * cookie if not present, and returns initial 'index.html' where the form is
+ * not yet initialized, and will be requested for the session
+ */
@cask.get("/")
def getIndex(ctx: cask.Request) = {
val sessionCookie = ctx.cookies.get(sessoinCookieName)
lazy val newSessionCookies = sessionCookie match {
- case None => Seq(cask.Cookie(sessoinCookieName, UUID.randomUUID().toString(), path = "/"))
+ case None =>
+ Seq(
+ cask.Cookie(
+ sessoinCookieName,
+ UUID.randomUUID().toString(),
+ path = "/"
+ )
+ )
case Some(_) => Seq.empty // don't set new cookies
}
@@ -45,7 +52,8 @@ case class Routes()(implicit cc: castor.Context, log: cask.Logger)
@cask.get("/force-new-session")
def forceNewSession() = {
- val newSessionCookie = cask.Cookie(sessoinCookieName, UUID.randomUUID().toString(), path = "/")
+ val newSessionCookie =
+ cask.Cookie(sessoinCookieName, UUID.randomUUID().toString(), path = "/")
println(s"setting new session ${newSessionCookie.value}")
cask.Response(
s"New session forced. Force new session",
@@ -54,18 +62,21 @@ case class Routes()(implicit cc: castor.Context, log: cask.Logger)
)
}
- /**
- * This method only works when cookie 'sessionId' is present
- * will get or init Form State for the session,
- * and return last unsubmitted form step fragment
- */
+ val formDataContextVarName = "formData"
+
+ /** This method only works when cookie 'sessionId' is present will get or init
+ * Form State for the session, and return last unsubmitted form step fragment
+ */
@cask.get("/get-form")
def getForm(sessionId: cask.Cookie) = {
val state = Models.Answers(currentStep = 1)
- cask.Response("yoyo")
val context = new Context()
- context.setVariable("formData", state)
- val formFragment = templateEngine.process(state.fragmentName, Set("formFragment").asJava, context)
+ context.setVariable(formDataContextVarName, state)
+ val formFragment = templateEngine.process(
+ state.fragmentName,
+ Set("formFragment").asJava,
+ context
+ )
cask.Response(
formFragment,
headers = Seq("Content-Type" -> "text/html;charset=UTF-8")
@@ -74,20 +85,29 @@ case class Routes()(implicit cc: castor.Context, log: cask.Logger)
// i guess let's make step a hidden input?
@cask.post("/submit-step/:stepNum")
- def submitStep(sessionId: cask.Cookie, stepNum: Int, request: cask.Request) = {
+ def submitStep(
+ sessionId: cask.Cookie,
+ stepNum: Int,
+ request: cask.Request
+ ) = {
val id = sessionId.value
- println(s"got $request for $id")
+ println(s"got $request for $id. it's data is ${request.text()}")
val userAnswers = Sessions.sessionReplies.getOrElse(id, Answers(id))
- // now i want to do what?
- // select 'answerStep' form userAnswers
- // delegate parsing and saving of the request.text to it
- // set current step to next
- // and return next rendered form step
- // with the 'form data' caclulated from the updated answers
-
- cask.Response(s"what i got is $request and ${request.text()}")
+ val updatedAnswers = userAnswers.updateStep(stepNum, request.text())
+ val context = new Context()
+ context.setVariable(formDataContextVarName, updatedAnswers)
+ val nextFormFragment = templateEngine.process(
+ updatedAnswers.fragmentName,
+ Set("formFragment").asJava,
+ context
+ )
+ println(s"the state now is $updatedAnswers")
+ cask.Response(
+ nextFormFragment,
+ headers = Seq("Content-Type" -> "text/html;charset=UTF-8")
+ )
}
@cask.staticResources("/public")