Introduction to Scala.js

Reading Time: 4 minutes

Scala.js

A safer way to build robust front-end web applications!

http://www.scala-js.org/

In this blog, we are gonna utilizing a whole lot of features of Scala to build a simple front-end application using Scala.js.

So, let’s get it started-

Step-1: – Creating an sbt project

Create a folder named scala-js-example and inside it create a build.sbt.

build.sbt

lazy val root = project.in(file("."))
  .settings(
    name := "scala-js-example",
    version := "0.0.1",
    scalaVersion := "2.13.3")

Now, create a new folder named as a project inside the scala-js-example folder and add these three files: –

build.properties

sbt.version = 1.4.1

plugins.sbt

addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.2.0")
addSbtPlugin("org.portable-scala" % "sbt-scalajs-crossproject" % "1.0.0")

Dependencies.scala

import org.portablescala.sbtplatformdeps.PlatformDepsPlugin.autoImport._
import sbt._

object Dependencies {

  private object versions {
    lazy val scalaJsDom: String = "1.1.0"
  }

  object js {
    lazy val scalaJsDom = Def.setting("org.scala-js" %%% "scalajs-dom" % versions.scalaJsDom)
  }

}

As you can see above, the Scala version for the project is 2.13.3, SBT version is 1.4.1 and to run the SBT I am using JVM-14.

The next step would be to execute sbt clean compile

For now, a basic sbt project has created, the next step would be to edit the build.sbt again.

build.sbt

import Dependencies._
import sbtcrossproject.CrossPlugin.autoImport.{crossProject => sbtCrossProject}

lazy val root = project.in(file("."))
  .aggregate(crossProjectJS, crossProjectJVM)
  .settings(
    name := "scala-js-example",
    version := "0.0.1",
    scalaVersion := "2.13.4")

lazy val crossProject = sbtCrossProject(JSPlatform, JVMPlatform).in(file("."))
  .settings(
    version := "0.0.1",
    scalaVersion := "2.13.3")

lazy val crossProjectJVM = crossProject.jvm

lazy val crossProjectJS = crossProject.js.settings(
  libraryDependencies ++= Seq(js.scalaJsDom.value),
  crossTarget in fastOptJS := baseDirectory.value / "src/main/resources/web/scala-js"
)

So, let’s try to understand build.sbt

We have added a cross-project plugin, to create a cross-project, this is done to better organize the code, which means, the code which will be designed to run Js Virtual machine will go inside js/src/main/scala, the code which will be designed to run Java Virtual machine will go inside jvm/src/main/scala and the common code between two of them will go inside the shared/src/main/scala.

For more info on cross-project, please visit: – https://www.scala-js.org/doc/project/cross-build.html

So, the project structure will gonna look like

scala-js-example
 +- jvm
 |   +- src/main/scala
 |      +- app
 |         +- Main.scala
 +- js
 |   +- src/main/resources
 |      +- web
 |         +- css
 |            +- main.css
 |         +- scala-js
 |         +- index.html
 |   +- src/main/scala
 |      +- handler
 |         +- ContainerEventHandler.scala
 |         +- WindowEventHandler.scala
 |      +- css
 |         +- classes
 |            +- package.scala
 +- shared
 |   +- src/main/resources
 |   +- src/main/scala
 |      +- logger
 |         +- Logger.scala
 +- project
 |   +- build.properties
 |   +- Dependencies.scala
 |   +- plugins.sbt
 +- build.sbt

Now, if you notice we have something like this inside build.sbt

crossTarget in fastOptJS := baseDirectory.value / "src/main/resources/web/scala-js"

This piece of block says to generate the resultant Js file inside js/src/main/resources/web/scala-js and from there we would be using this Js file to our HTML code.

The project structure is created now and let’s move towards the code

Step-2: – Writing scala code

jvm

src\main\scala\app\Main.scala
package app

import com.knoldus.logger.Logger

/**
 * Main from jvm.
 */
object Main extends App {

  val message = "SCALA-JS-EXAMPLE"
  Logger.info(message)
}

js

src\main\scala\handler\ContainerEventHandler.scala
package handler

import css.classes._
import com.knoldus.logger.Logger
import org.scalajs.dom._

import scala.scalajs.js.annotation.JSExportTopLevel

/**
 * This event handler will handle events generated from `container`.
 *
 * Note:- `container` is the id assigned to `div` element inside `src\main\resources\web\index.html`.
 */
object ContainerEventHandler {

  @JSExportTopLevel("handleContainerButtonClickEvent")
  def handleButtonClickEvent(): Unit = {
    val bodyClassList = document.querySelector("body").classList
    bodyClassList.toggle(LINER_GRADIENT_DESIGN_BOTTOM_LEFT)
    bodyClassList.toggle(LINER_GRADIENT_DESIGN_TOP_RIGHT)
    Logger.info("Gradient designed changed")
  }
}
src\main\scala\handler\WindowEventHandler.scala
package handler

import css.classes._
import com.knoldus.logger.Logger
import org.scalajs.dom._

import scala.scalajs.js.annotation.JSExportTopLevel

/**
 * This Event handler will handle events from `window` object.
 * Ex:- window.onload = onLoadHandler
 */
object WindowEventHandler {

  @JSExportTopLevel("onLoadHandler")
  def onLoadHandler(): Unit = {
    val webPageLoadedMessage = "Web page loaded successfully."
    window.alert(webPageLoadedMessage)
    document.querySelector("#container").classList.remove(DISPLAY_NONE)
    Logger.info(webPageLoadedMessage)
  }
}
src\main\scala\css\classes\package.scala
package css

/**
 * This object contains all name of the global css classes present inside
 * `src\main\resources\web\css\main.css`.
 */
package object classes {

  lazy val DISPLAY_NONE: String = "display-none"
  lazy val LINER_GRADIENT_DESIGN_BOTTOM_LEFT: String = "linear-gradient-design-bottom-left"
  lazy val LINER_GRADIENT_DESIGN_TOP_RIGHT: String = "linear-gradient-design-top-right"
  lazy val COLUMN_FLEX_CONTAINER: String = "column-flex-container"
  lazy val FULL_WIDTH_HEIGHT: String = "full-width-height"
}
src\main\resources\web\css\main.css
html {

    width: 100%;
    height: 100%;
}

body {

    overflow:hidden;
}

.linear-gradient-design-bottom-left {

    background-image: linear-gradient(to bottom left, #66ffff, #ff66ff);
}

.linear-gradient-design-top-right {

    background-image: linear-gradient(to top right, #66ffff, #ff66ff);
}

.display-none {

    display:none
}

.column-flex-container {

    display: flex;
    flex-direction: column;
    justify-content: flex-start;
}

.full-width-height {

    width: 100%;
    height: 100%;
}

#container > button {

    margin: auto;
    font-size: xx-large;
    background-color: white;
}
src\main\resources\web\index.html
<!DOCTYPE html>

<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>scala-js-example</title>
        <link rel="stylesheet" href="css/main.css">
    </head>

    <body class="linear-gradient-design-bottom-left">
        <div id="container" class="display-none column-flex-container full-width-height">
            <h1>Welcome to <b>scala-js</b></h1>
            <button onclick="handleContainerButtonClickEvent()">Click Me</button>
        </div>

        http://scala-js/scala-js-example-fastopt.js
        <script>
            window.onload = onLoadHandler
        </script>
    </body>
</html>

shared

src\main\scala\logger\Logger.scala
package logger

/**
 * Sample Logger to log output in browser for scala-js and on console for scala-jvm.
 */
object Logger {

  def info(messages: Any*): Unit = println(messages.map(_.toString).mkString(","))
}

Now we are all set and let’s go ahead and run this

Step-2: – Executing the project

jvm

Steps to execute
sbt "project crossProjectJVM" run
Output
SCALA-JS-EXAMPLE

js

Steps to execute
sbt fastOptJS

After this, you can see that the scala-js folder inside js/src/main/resources/web will contain a Js file named scala-js-example-fastopt.js

Now let just go to your index.html file inside js/src/main/resources/web and update the src attribute of the script tag to point to the generated Js file

Like for example, as mentioned above inside index.html

http://scala-js/scala-js-example-fastopt.js

Open index.html file with a web browser and you can see that

  1. A alert box at start-up
  2. Background color change on every button click
  3. Open the Js console inside browser inspect and there you can see the output of println.

Hope this blog helps you to understand the basic concepts of Scala.js.

2 thoughts on “Introduction to Scala.js6 min read

Comments are closed.

%d bloggers like this: