The Client
As is typical for SPAs the client consists of a single HTML file and a number of supporting resources (JS and CSS). One of these resources is the actual JavaScript
code generated by Scala.js from the Scala sources. The HTML is defined under the server project in twirl/views/index.scala.html
using Play template system.
Here you'll find the usual HTML things like links to CSS and JS files. As you can see, the <body>
element is pretty empty, because all the
HTML will be generated by the application itself. The @scalajs
directive tells the ScalaJSWeb plugin
to insert relevant <script>
tags to load the generated JavaScript code and its dependencies. For your convenience, the _asset
function prefixes your assets (css, js, images)
with a CDN link of your choice defined in server/src/main/resources/application.conf. Note that the line with "${?APPLICATION_CDN}" is one way to override default values
in production with environment variables if they are defined. The _asset
function also adds a version to the file name automatically converting main.min.css to main-<version>
.min.css in production
which clears client proxy and browser cache when you make changes to the file. See PlayFramework Fingerprinting
for more information about this best practice.
<body>
<div id="root">
</div>
@scalajs.html.scripts(projectName = "client", name => _asset(name).toString, name => getClass.getResource(s"/public/$name") != null)
</body>
</html>
Instead of using external JavaScript references to React, jQuery, Bootstrap and to a chart component, the build system combines all these into a single JavaScript file (client-jsdeps.js). See here for details. The last JavaScript reference is the compiled application code.
Once the browser has loaded all the resources, it will call the SPAMain().main()
method defined in the
SPAMain.scala
singleton class. This is the
entry point of the application. The class itself is very simple,
@JSExport("SPAMain")
object SPAMain extends JSApp {
@JSExport
def main(): Unit = {
// create stylesheet
GlobalStyles.addToDocument()
// create the router
val router = Router(BaseUrl.until_#, routerConfig)
// tell React to render the router in the document body
React.render(router(), dom.document.getElementById("root"))
}
}
The externally accessible classes and functions are annotated with @JSExport
so the Scala.js compiler knows not to optimize them away and make them available
with those exact names in the global scope.
What main()
does is simply to create a router and instruct React to render it inside the document <div id="root">
tag.
Now at this point you've seen couple of references to React and might wonder what's it about. React is a JavaScript library for building user interfaces, developed by Facebook. You might ask "why use a JavaScript library with Scala.js if Scala is so great" and yes, it might make sense to do a similar library in Scala.js but since it's already there, why not use it. And to sweeten the deal there is a very nice wrapper for React called scalajs-react by David Barri (@japgolly).
There are also other Scala.js libraries available for building SPAs but I wanted to go with React, so that's what we'll use in this tutorial :)