Techniques
Techniques strongly tied to OAT.
Shorter stuff can go here. Longer or more important stuff may get their own page.
Delivering data to the browser
When a web page has dynamic data to display, there are several general approaches:
-
Render it all on the server. Ask the server for an updated page when anything changes (Web1.0).
-
Deliver an HTML shell of the page and some ScalaJS. Fetch the data needed from a web service and render it on the browser.
-
Embed the data in the HTML. Use ScalaJS to extract it and render the page.
-
Render as much on the server as you can.
Approaches 2, 3, and 4 assume you have ScalaJS/Javascript running to add interactivity in addition to (possibly) rendering the page.
I (Byron) had assumed/thought for quite a while that #2 was the preferred way to go. We have lots of #3 thanks to some coop students, but I was suspicious. A chat with Claude.ai in 2025 suggests real advantages (faster load/render) for #3 over #2. It also said that the current best practise was #4. Render as much as you can on the server, adding Javascript for interactivity and possibly rerendering the page with newly fetched data. However, I don’t see enough advantage to move in that direction.
Digging into #3… In the views delivered by
the wapp controller, look for withData. For example, in
oat.wapp.views.forms.FormsSubmittedView we have
Template("Submitted Forms")
.withJS(npm.sortable)
.withData(
"formDefinition" -> Json.toJson(form),
"submissions" -> Json.toJson(submissions),
"hasFormsAdminPerm" -> JsBoolean(context.hasPermission(Permission.Forms))
)
.renderThis embeds the form submission data in the HTML for retrieval on the browser.
In the browser’s version of this class, we have
private val model = SubmittedModel(
context[Userid]("userid"),
context[Form]("formDefinition"),
context[Vector[Submission]]("submissions"),
context[Boolean]("hasFormsAdminPerm")
)The userid was added automatically by the template along with name, whether
this is the production instance, a csrf token, and if the user has an instructor or
advisor role. The context function extracts the data with the given name and
converts it to the specified type.