JavaScript

Creating an Angular.js application without JavaScript: Scala.js

In my day job I work a lot with Angular.js. And while this is a great framework, sometimes I do miss the type-safety and advanced features of languages such as Scala. Getting “undefined is not a function” is not the the most informative error message you get when developing JavaScript applications.

A couple of weeks ago I ran across a twitter message showing that Scala.js 0.60 (http://www.scala-js.org/) was released. With Scala.js you can compile Scala directly to JavaScript. So you can use all the advanced library and language features of Scala, and create JavaScript applications in a typesafe, functional manner!

So for this article I’ll show you the steps I went through to create a minimal Angular.js application using nothing but Scala (and some HTML templates of course).

http://www.smartjava.org/examples/scalajs/html/index-dev.html#/home

Minimal_angular_js

This Angular / Scala.js app will show the following features:

  • Controllers written in Scala
  • Directives written in Scala
  • Filters written in Scala
  • Use the Angular.js route module for handling URL paths
  • Use foundation for templating

You can find the complete sources for this application in GitHub (https://github.com/josdirksen/smartjava/tree/master/scalajs).

Lets get started

The first thing we need to do is make sure we have node.js installed. This isn’t really necessary but will speed up development considerably. So install node.js from here (https://nodejs.org/download/) before you continue. To work with Scala.js we have to install an sbt plugin. In the file plugins.sbt in the project directory add the following line:

addSbtPlugin('org.scala-js' % 'sbt-scalajs' % '0.6.0')

Note that I’ve tested this with sbt 0.13.7, so make sure you’ve got a current version of sbt installed. Next lets look at the build.sbt file we’ve used for this project:

enablePlugins(ScalaJSPlugin)

name := "scalajs"

version := "1.0"

scalaVersion := "2.11.5"

libraryDependencies += "org.scala-js" %%% "scalajs-dom" % "0.8.0"
libraryDependencies += "com.greencatsoft" %%% "scalajs-angular" % "0.4-SNAPSHOT"

resolvers +=
  "Sonatype OSS Snapshots" at "https://oss.sonatype.org/content/repositories/snapshots"

jsDependencies += "org.webjars" % "angularjs" % "1.3.14" / "angular-route.js" dependsOn "angular.js"
jsDependencies += "org.webjars" % "angularjs" % "1.3.14" / "angular.js"
jsDependencies += "org.webjars" % "angular-foundation" % "0.3.0" / "mm-foundation.js"
jsDependencies += "org.webjars" % "angular-foundation" % "0.3.0" / "mm-foundation-tpls.js"

persistLauncher in Compile := true
persistLauncher in Test := false

skip in packageJSDependencies := false 

In this build.sbt you can see the following:

  1. We enable the scalaJS Plugin
  2. We include a couple of Scala.js dependencies like we normally do for scala
  3. We also include a number of jsDependencies. These are the JavaScript libraries we want to use in our web application
  4. And finally we configure how the application will be packaged. More on that later

Lets look a bit at the last couple of lines of this build file:

persistLauncher in Compile := true
skip in packageJSDependencies := false 

By setting the persistLauncher to true, we tell Scala.js to create the JavaScript that will automatically launch our application and by setting skip in packageJSDependencies to false, we tell Scala.js to package all our external JavaScript files into one big library for easy inclusion in your HTML page.

Before we look at the Scala code and the HTML templates lets first look at a couple of useful SBT commands. Go to the directory where your project is and run sbt to open the sbt console:

jos@Joss-MacBook-Pro.local:~/git/scalajs$ sbt
[info] Set current project to scalajs (in build file:/Users/jos/git/scalajs/)

Now to generate the JavaScript code, we first have to enable node.js so that creating the JavaScript goes a whole lot quicker. In the sbt console set the following:

> set scalaJSStage in Global := FastOptStage
[info] Defining */*:scalaJSStage
[info] The new value will be used by compile:jsEnv, compile:scalaJSExecClasspath and 1 others.
[info]  Run `last` for details.
[info] Reapplying settings...
[info] Set current project to scalajs (in build file:/Users/jos/git/scalajs/)
>

Now you can compile the Scala to JavaScript by calling fastOptJS

>  fastOptJS
[warn] The global sbt directory is now versioned and is located at /Users/jos/.sbt/0.13.
[warn]   You are seeing this warning because there is global configuration in /Users/jos/.sbt but not in /Users/jos/.sbt/0.13.
[warn]   The global sbt directory may be changed via the sbt.global.base system property.
[info] Fast optimizing /Users/jos/git/scalajs/target/scala-2.11/scalajs-fastopt.js
[success] Total time: 7 s, completed Mar 8, 2015 8:01:31 AM

Enough about SBT, lets look at the Scala code

Angular App in Scala

The complete file can be found in Git (https://github.com/josdirksen/smartjava/tree/master/scalajs) so we won’t show that here. We’ll just look at the interesting parts. Before we look at the JavaScript though, lets look at the HTML templates:

<!DOCTYPE html>
<html ng-app="helloworld">
<head>
    <meta charset="UTF-8">
    <title>Minimal angular.js</title>
    <link rel="stylesheet" href="css/foundation.css" />
    <style>
        .tile {
            background: #ffeeee;
        }
    </style>
</head>
<body>
 
<div class="row">
    <div class="large-12 columns">
        <h1>Sample angular.js / foundation / scala.js app</h1>
    </div>
</div>
 
<div ng-view></div>
 
<!-- Include Scala.js compiled code -->
<script type="text/javascript" src="../../../target/scala-2.11/scalajs-fastopt.js"></script>
<script type="text/javascript" src="../../../target/scala-2.11/scalajs-jsdeps.js"></script>
<script type="text/javascript" src="../../../target/scala-2.11/scalajs-launcher.js"></script>
 
</body>
 
</html> 

This file is our main index file. As you can see, not that special. Just a single div where we have an ng-view. What is interesting are the three JavaScript files at the bottom of the page. These are generated by Scala.js and contain all our code:

  • scalajs-fastopt.js: our compiled application.
  • scalajs-jsdeps.js: all dependencies as a single file
  • scalajs-launcher.js: code that will launch our application when the page is loaded

We have two additional HTML files, one that is shown as the main page in the ng-view we just saw, and one that is used as the tiles you saw in the beginning of this article:

<!DOCTYPE html>
<html ng-app="helloworld">
<head>
    <meta charset="UTF-8">
    <title>Minimal angular.js</title>
    <link rel="stylesheet" href="css/foundation.css" />
    <style>
        .tile {
            background: #ffeeee;
        }
    </style>
</head>
<body>
 
<div class="row">
    <div class="large-12 columns">
        <h1>Sample angular.js / foundation / scala.js app</h1>
    </div>
</div>
 
<div ng-view></div>
 
<!-- Include Scala.js compiled code -->
<script type="text/javascript" src="../../../target/scala-2.11/scalajs-fastopt.js"></script>
<script type="text/javascript" src="../../../target/scala-2.11/scalajs-jsdeps.js"></script>
<script type="text/javascript" src="../../../target/scala-2.11/scalajs-launcher.js"></script>
 
</body>
 
</html> 
<p simple-directive="some configuration"></p> 

So now that we’ve seen the HTML, it is time for the actual code. The first part we’ll look at is the starting point of any Angular application, the module configuration:

object HelloWorld extends JSApp {
 
  def main(): Unit = {
    val module = Angular.module("helloworld",Seq("ngRoute","mm.foundation"))
    module.controller(HomeController)
    module.config(RoutingConfig)
    module.filter(ToUpperFilter)
    module.filter(IntDoublerFilter)
    module.directive(SimpleDirective)
  }
} 

Here we define our Angular module (named helloworld) and add two dependencies: ngRoute and mm.foundation. ngRoute provides us with a way to easily configure URL mapping to templates and controllers, and mm.foundation (https://github.com/pineconellc/angular-foundation/tree/gh-pages) provides us with directives that map to foundation (http://foundation.zurb.com/) widgets.

Next we link to the controllers, filters, directives, config objects we want to use in this app. Note that these are all statically typed, so you can’t forget or register angular components incorrectly. In this example we have five additional components, lets look at each one. First lets look at the RoutingConfig:

object RoutingConfig extends Config {
 
  @inject
  var routeProvider: RouteProvider = _
 
  override def initialize() {
    routeProvider
      .when("/home", Route(HomeController))
  }
} 

As you can see, this is very easy and pretty much the same as you’d do it in JavaScript. The main difference is that instead of providing string values in JavaScript we now just reference a specific controller to use for a path.This controller, which we registered earlier, looks like this:

object HomeController extends PageController {
 
  import js.JSConverters._
 
  val templateUrl = "templates/home.html"
  override type ScopeType = ControllerData
 
  @inject
  var service: Interval = _
 
  override def initialize(scope: ScopeType): Unit = {
    scope.title = "This is the title"
    scope.subTitle = "Make Me Big"
 
    scope.anotherScope = 1 to 12 toJSArray
 
    service.apply( () => scope.count = System.currentTimeMillis() % 10000, 100)
  }
 
  /**
   * The specific scope data used in this controller
   */
  trait ControllerData extends Scope {
    var title: String = js.native
    var subTitle: String = js.native
    var count: Double = js.native
 
    var anotherScope: js.Array[Int] = js.native
  }
} 

I won’t go into too much detail here, since the code is pretty self-explanatory. What you can see here is that we define the template that we want to show (when the url from the route is accessed), we setup some scope variables (all typed!) and start an interval using the injected Interval service.

If you’ve looked through the pages you can also see we use a number of filters. Defining a filter in Scala.js is really easy:

/**
 * Simple filter: text to uppercase
 */
object ToUpperFilter extends Filter {
  override val name = "upper"
  override def filter(text: String): String = text.toUpperCase
}
 
/**
 * Simple filter: double a value
 */
object IntDoublerFilter extends Filter {
  override val name = "double"
  override def filter(text: String): String = text.toInt*2 toString
} 

And finally we have a directive which we use in tile.html.

object SimpleDirective extends AttributeDirective {
 
  override val name = "simpleDirective"
 
  @JSExportAll
  case class Address(ip : String)
 
  override def link(scope: ScopeType, elems: Seq[Element], attrs: Attributes, controller: Controller*) = {
    val elem = elems.head.asInstanceOf[HTMLElement]
    elem.textContent = "Some content set from the directive"
  }
} 

When you compile this application to JavaScript using sbt and open the HTML pages you’ll get what we showed in the beginning. A very basic Angular.js application with custom filters, directives and two Angular extensions.

Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button