summaryrefslogtreecommitdiff
path: root/book/src/main/scalatex/book/handson
diff options
context:
space:
mode:
Diffstat (limited to 'book/src/main/scalatex/book/handson')
-rw-r--r--book/src/main/scalatex/book/handson/CanvasApp.scalatex20
-rw-r--r--book/src/main/scalatex/book/handson/ClientServer.scalatex6
-rw-r--r--book/src/main/scalatex/book/handson/CommandLine.scalatex12
-rw-r--r--book/src/main/scalatex/book/handson/GettingStarted.scalatex44
-rw-r--r--book/src/main/scalatex/book/handson/PublishingModules.scalatex18
-rw-r--r--book/src/main/scalatex/book/handson/WebPage.scalatex38
6 files changed, 69 insertions, 69 deletions
diff --git a/book/src/main/scalatex/book/handson/CanvasApp.scalatex b/book/src/main/scalatex/book/handson/CanvasApp.scalatex
index 03a3779..5f40270 100644
--- a/book/src/main/scalatex/book/handson/CanvasApp.scalatex
+++ b/book/src/main/scalatex/book/handson/CanvasApp.scalatex
@@ -2,7 +2,7 @@
@p
By this point, you've already cloned and got your hands dirty fiddling around with the toy @a("workbench-example-app", href:="https://github.com/lihaoyi/workbench-example-app"). You have your editor set up, SBT installed, and have published the example application in a way you can host online for other people to see. Maybe you've even made some changes to the application to see what happens. Hopefully you're curious, and want to learn more.
-@p
+@p
In this section of the book, we will walk through making a small canvas application. This will expose you to important concepts like:
@ul
@@ -49,7 +49,7 @@
Apart from mouse events, keyboard events, scroll events, input events, etc. are all usable from Scala.js as you'd expect
@sect{Making a Clock using setInterval}
-
+
@p
You've already seen this in the previous example, but @hl.scala{dom.setInterval} can be used to schedule recurring, periodic events in your program. Common use cases include running the event loop for a game, making smooth animations, and other tasks of that sort which require some work to happen over a period of time.
@@ -84,7 +84,7 @@
In this example we will make a spiritual clone of the popular @a("Flappy Bird", href:="http://en.wikipedia.org/wiki/Flappy_Bird") video game. This game involves a few simple rules
@ul
- @li
+ @li
Your character starts in the middle of the screen
@li
Gravity pulls your character down
@@ -111,7 +111,7 @@
@hl.ref("examples/demos/src/main/scala/canvasapp/FlappyLine.scala", "/*variables*/", end="def runLive")
@p
- This is where we start defining things that are relevant to Flappy Box. There are roughly two groups of values here: immutable constants in the top group, and mutable variables in the bottom. The rough meaning of each variable is documented in the comments, and we'll see exactly how we use them later.
+ This is where we start defining things that are relevant to Flappy Box. There are roughly two groups of values here: immutable constants in the top group, and mutable variables in the bottom. The rough meaning of each variable is documented in the comments, and we'll see exactly how we use them later.
@p
One notable thing is that we're using a @hl.scala{collection.mutable.Queue} to store the list of obstacles. This is defined in the Scala standard library; in general, all the collections in the Scala standard library can be used without issue in Scala.js.
@@ -134,7 +134,7 @@
@li
Rendering everything, including the player as the namesake box
- @p
+ @p
This function basically contains all the game logic, from motion, to collision-detection, to rendering, so it's pretty large. Not that large though! And entirely understandable, even if it takes a few moments to read through.
@hl.ref("examples/demos/src/main/scala/canvasapp/FlappyLine.scala", "def runDead", "def run()")
@@ -181,14 +181,14 @@
@hl.scala{obstacles} is a Scala @hl.scala{mutable.Queue}, as we defined it earlier, and all the methods on it are Scala method calls
@li
@hl.scala{renderer} is a Javascript @hl.javascript{CanvasRenderingContext2D}, and all the methods on it are Javascript method calls directly on the Javascript object
- @li
+ @li
@hl.scala{frame} is a Scala @hl.scala{Int}, and obeys Scala semantics, though it is implemented as a Javascript @hl.javascript{Number} under the hood.
- @li
+ @li
@hl.scala{playerY} and @hl.scala{playerV} are Scala @hl.scala{Double}s, implemented directly as Javascript @hl.javascript{Number}s
@p
- This reveals something pretty interesting about Scala.js: even though Scala at-first-glance is a very different language from Javascript, the interoperation with Javascript is so seamless that you can't even tell from the code which values/methods are defined in Scala and which values/methods come from Javascript!
- @p
+ This reveals something pretty interesting about Scala.js: even though Scala at-first-glance is a very different language from Javascript, the interoperation with Javascript is so seamless that you can't even tell from the code which values/methods are defined in Scala and which values/methods come from Javascript!
+ @p
These two classes of values/methods are treated very differently by the compiler when it comes to emitting the executable Javascript blob, but the compiler does not need extra syntax telling it which things belong to Scala and which to Javascript: the types are sufficient. @hl.scala{renderer}, for example is of type @hl.scala{dom.CanvasRenderContext2D} which is a subtype of @hl.scala{scalajs.js.Object}, indicating to the compiler that it needs special treatment. Primitives like @hl.scala{Double}s and @hl.scala{Int}s have similar treatment
@p
@@ -209,5 +209,5 @@
@li
Draw something pretty! We have a working canvas, a nice programming language, and a way to easily publish the results online. @a("Various", href:="http://www.scala-js-fiddle.com/gist/77a3840678d154257ca1/KochSnowflake.scala") @a("fractals", href:="http://www.scala-js-fiddle.com/gist/77a3840678d154257ca1/KochCurve.scala"), or @a("colorful visualizations", href:="http://www.scala-js-fiddle.com/gist/9443f8e0ecc68d1058ad/RayTracer.scala") are all possibilities.
- @p
+ @p
By this point you've some experience building stand-alone, single-canvas Scala.js applications, which has hopefully given you a feel for how Scala.js works. The problem is that few web applications satisfy the criteria of being stand-alone single-page canvas applications! Most web applications need to deal with the DOM of the HTML page, need to fetch data from web services, and generally need to do a lot of other messy things. We'll go into that in the next chapter
diff --git a/book/src/main/scalatex/book/handson/ClientServer.scalatex b/book/src/main/scalatex/book/handson/ClientServer.scalatex
index 817f568..771dbde 100644
--- a/book/src/main/scalatex/book/handson/ClientServer.scalatex
+++ b/book/src/main/scalatex/book/handson/ClientServer.scalatex
@@ -31,7 +31,7 @@
└── src/main/scala/simple
├── Page.scala
└── Server.scala
-
+
@p
First, let's do the wiring in @code{build.sbt}:
@@ -41,7 +41,7 @@
We have two projects: @code{client} and @code{server}, one of which is a Scala.js project (indicated by the presence of @hl.scala{scalaJSSettings}). Both projects share a number of settings: the presence of the @code{shared/} folder, which shared code can live in (similar to what we saw in Cross-platform Modules) and the settings to add Scalatags and uPickle to the build. Note that those two dependencies use the triple @code{%%%} instead of the double @code{%%} to declare: this means that for each dependency, we will pull in the Scala-JVM or Scala.js version depending on whether it's being used in a Scala.js project.
@p
The @code{client} subproject is uneventful, with a dependency on the by-now-familiar @code{scalajs-dom} library. The @code{server} project, on the other hand, is interesting: it contains the dependencies required for us to set up out Spray server, and one additioanl thing: we add the output of @code{fastOptJS} from the client to the @code{resources} on the server. This will allow the @code{server} to serve the compiled-javascript from our @code{client} project from its resources.
-
+
@p
Next, let's kick off the Spray server in our Scala-JVM main method:
@@ -92,7 +92,7 @@
@sect{Boilerplate-free Serialization}
@p
- The Ajax/RPC layer is one of the more fragile parts of web applications. Often, you have your various Ajax endpoints written once on the server, have a set of routes written to connect those Ajax endpoints to URLs, and client code (traditionally Javascript) made calls to those URLs with "raw" data: basically whatever you wanted, packed in an ad-hoc mix of CSV and JSON and raw-strings.
+ The Ajax/RPC layer is one of the more fragile parts of web applications. Often, you have your various Ajax endpoints written once on the server, have a set of routes written to connect those Ajax endpoints to URLs, and client code (traditionally Javascript) made calls to those URLs with "raw" data: basically whatever you wanted, packed in an ad-hoc mix of CSV and JSON and raw-strings.
@p
This has always been annoying boilerplate, and Scala.js removes it. With uPickle, you can simply call @hl.scala{upickle.write(...)} and @hl.scala{upickle.read[T](...)} to convert your collections, primitives or case-classes to and from JSON. This means you do not need to constantly re-invent different ways of making Ajax calls: you can just fling the data right across the network from client to server and back again.
diff --git a/book/src/main/scalatex/book/handson/CommandLine.scalatex b/book/src/main/scalatex/book/handson/CommandLine.scalatex
index dffeaaf..1bffef2 100644
--- a/book/src/main/scalatex/book/handson/CommandLine.scalatex
+++ b/book/src/main/scalatex/book/handson/CommandLine.scalatex
@@ -18,7 +18,7 @@
@code{package}: bundling up our Scala.js IR into a @code{.jar} file, for publishing or distribution as a library
@li
@code{fastOptJS}: aggregating our Scala.js IR and converting it to a @code{.js} executable.
- @li
+ @li
@code{fullOptJS}: aggregating our Scala.js IR and converting it to a smaller, faster @code{.js} executable.
@li
@code{run}: run your compiled Scala.js code as Javascript in Rhino, Node.js or Phantom.js
@@ -94,7 +94,7 @@
@p
Again, I won't go into much details, as exactly what this optimization does is described in the chapter on the Compilation Pipeline. This command is somewhat too-slow to be running during iterative development, and is instead typically used just before deployment to minimize the size of the file your users have to download.
- @sect{The run Command}
+ @sect{The run Command}
@hl.bash
> run
@p
@@ -111,7 +111,7 @@
@p
Running @code{sbt run} with the above Scala.js code will print out
-
+
@hl.bash
Hello World!
In Scala.js, 1/0 is 0!
@@ -133,12 +133,12 @@
@p
Typically, the best way to get started is using Rhino and @code{sbt run}, since it's setup-free, and setting up Node.js or PhantomJS later as necessary. The next two sections elaborate on the differences between these ways of running your code. Check out the later sections on Headless Runtimes and Run Configurations to learn more about the other settings and why you would want to use them.
- @sect{The test Command}
+ @sect{The test Command}
@hl.bash
> test
@p
- The @code{sbt test} command behaves very similar to @code{sbt run}. It can also be prefixed e.g. @code{sbt fastOptStage::run} or @code{sbt fullOptStage::run}, or run in either Rhino, Node.js or PhantomJS.
+ The @code{sbt test} command behaves very similar to @code{sbt run}. It can also be prefixed e.g. @code{sbt fastOptStage::run} or @code{sbt fullOptStage::run}, or run in either Rhino, Node.js or PhantomJS.
@p
The difference is that instead of simply running your @code{main} method, @code{sbt test} runs whatever test-suite you have set-up, which will look through your @code{tests/} folder to find suites of tests it has to run, and aggregate the results formatted nicely for you to see. The exact operation of this depends on which testing library you're using
@p
@@ -161,7 +161,7 @@
@ul
@li
- @code{sbt run}: this does not perform any optimization of the output Scala.js files, and does lazy-loading to minimize the amount of files being loading into the interpreter. This is necessary for Rhino because it can't handle large blobs of Javascript, but doesn't map to any compilation mode you'd use for the browser.
+ @code{sbt run}: this does not perform any optimization of the output Scala.js files, and does lazy-loading to minimize the amount of files being loading into the interpreter. This is necessary for Rhino because it can't handle large blobs of Javascript, but doesn't map to any compilation mode you'd use for the browser.
@li
@code{sbt fastOptStage::run}: this performs the same compilation and optimization as @code{sbt fastOptJS}, as described under Fast Optimizations. It then takes the entire output executable (which probably weighs around 1mb) and hands it to Node.js or PhantomJS, which then run it.
@li
diff --git a/book/src/main/scalatex/book/handson/GettingStarted.scalatex b/book/src/main/scalatex/book/handson/GettingStarted.scalatex
index ae97eb2..0ad211f 100644
--- a/book/src/main/scalatex/book/handson/GettingStarted.scalatex
+++ b/book/src/main/scalatex/book/handson/GettingStarted.scalatex
@@ -3,15 +3,15 @@
To get started with Scala.js, you will need to prepare a few things:
@ul
- @li
+ @li
@a("sbt", href:="http://www.scala-sbt.org/"): SBT is the most common build-tool in the Scala community, and is what we will use for building our Scala.js application. Their home page has a link to download and install it.
- @li
- An editor for Scala: @a("IntelliJ Scala", href:="http://blog.jetbrains.com/scala/") and @a("Eclipse ScalaIDE", href:="http://scala-ide.org/") are the most popular choices and work on all platforms, though there are others.
+ @li
+ An editor for Scala: @a("IntelliJ Scala", href:="http://blog.jetbrains.com/scala/") and @a("Eclipse ScalaIDE", href:="http://scala-ide.org/") are the most popular choices and work on all platforms, though there are others.
@li
@a("Git", href:="http://git-scm.com/"): This is a version-control system that we will use to download and manage our Scala.js projects.
- @li
+ @li
A terminal: on OSX you have @a("Terminal.app", href:="http://guides.macrumors.com/Terminal") already installed, in Linux you have @a("Terminal", href:="https://help.ubuntu.com/community/UsingTheTerminal"), and on Windows you have @a("PowerShell", href:="http://en.wikipedia.org/wiki/Windows_PowerShell").
- @li
+ @li
Your favorite web browser: @a("Chrome", href:="https://www.google.com/chrome") and @a("Firefox", href:="https://www.mozilla.org/en-US/firefox") are the most popular.
@p
@@ -58,7 +58,7 @@
[success] Total time: 11 s, completed Oct 26, 2014 3:42:21 PM
1. Waiting for source changes... (press enter to interrupt)
-@p
+@p
The line @code{Waiting for source changes...} is telling you that your Scala.js program is ready! Now, when you go to the web URL @code{http://localhost:12345/target/scala-2.11/classes/index-dev.html} in your browser, you should see the following:
@img(src:="images/Hello World.png", maxWidth:="100%")
@@ -98,7 +98,7 @@
@p
We've downloaded, compiled, ran, and made changes to our first Scala.js application. Let's now take a closer look at the code that we just ran:
- @hl.ref("output/workbench-example-app/src/main/scala/example/ScalaJSExample.scala")
+ @hl.ref("output/workbench-example-app/src/main/scala/example/ScalaJSExample.scala")
@p
It's a good chunk of code, though not a huge amount. To someone who didn't know about Scala.js, they would just think it's normal Scala, albeit with this unusual @hl.scala{dom} library and a few weird annotations. Let's pick it apart starting from the top:
@@ -124,7 +124,7 @@
@p
@hl.scala{document.getElementById} is the exact same API that's used in normal Javascript, as documented @a("here", href:="https://developer.mozilla.org/en-US/docs/Web/API/document.getElementById"). In fact, the entire @hl.scala{org.scalajs.dom} namespace (imported at the top of the file) comprises statically typed facades for the javascript APIs provided by the browser.
- @p
+ @p
We need to perform the @hl.scala{asInstanceOf} call because depending on what you pass to @hl.scala{getElementById} and @hl.scala{getContext}, you could be returned elements and contexts of different types. Hence we need to tell the compiler explicitly that we're expecting a @hl.scala{dom.HTMLCanvasElement} and @hl.scala{dom.CanvasRenderingContext2D} back from these methods for the strings we passed in.
@hl.ref("output/workbench-example-app/src/main/scala/example/ScalaJSExample.scala", "def run", "dom.setInterval")
@@ -133,13 +133,13 @@
This is the part of the Scala.js program which does the real work. It runs 10 iterations of a @a("small algorithm", href:="http://en.wikipedia.org/wiki/Sierpinski_triangle#Chaos_game") that generates a Sierpinski Triangle point-by-point. The steps, as described by the linked article, are roughly:
@ul
- @li
+ @li
Pick a random corner of the large-triangle
- @li
+ @li
Move your current-position @hl.scala{p} halfway between its current location and that corner
- @li
+ @li
Draw a dot
- @li
+ @li
Repeat
@p
@@ -164,17 +164,17 @@
@p
Of the two, only the Scala.js plugin is really necessary. The Workbench plugin is a convenience that makes development easier. Without it you'd need to keep a terminal open to view the SBT logspam, and manually refresh the page when compilation finished. Not the end of the world.
-
+
@sect{build.sbt}
@hl.ref("output/workbench-example-app/build.sbt")
@p
The @code{build.sbt} project file for this application is similarly unremarkable: It includes the settings for the two SBT plugins we saw earlier, as well as boilerplate @hl.scala{name}/@hl.scala{version}/@hl.scala{scalaVersion} values common to all projects.
- @p
+ @p
Of interest is the @hl.scala{libraryDependencies}. In Scala-JVM, this key is used to declare dependencies on libraries from Maven Central, so you can use them in your Scala-JVM projects. In Scala.js, the same key is used to declare dependencies on libraries so you can use them in your Scala.js projects! Re-usable libraries can be built and published with Scala.js just as you do on Scala-JVM, and here we make use of one which provides the typed facades with which we used to access the DOM in the application code.
- @p
+ @p
Lastly, we have two Workbench related settings: @hl.scala{bootSnippet} basically tells Workbench how to restart your application when a new compilation run finishes, and @hl.scala{updateBrowsers} actually tells it to perform this application-restarting.
@sect{src/main/resources/index-dev.html}
@@ -185,7 +185,7 @@
@p
The @hl.scala{ScalaJSExample().main()} call is what kicks off the Scala.js application and starts its execution. Scala.js follows Scala semantics in that @hl.scala{object}s are evaluated lazily, with no top-level code allowed. This is in contrast to Javascript, where you can include top-level statements and object-literals in your code which execute immediately. In Scala.js, nothing happens when @code{../example-fastopt.js} is imported! We have to call the main-method first. In this case, we're passing the canvas object (attained using @hl.javascript{getElementById}) to it so it knows where to do its thing.
-
+
@p
Lastly, only @hl.scala("@JSExport")ed objects and methods can be called from Javascript. Also, although this example only exports the @hl.scala{main} method which is called once, there is nothing stopping you from exporting any number of objects and methods and calling them whenever you need to. In this way, you can easily make a Scala.js "library" which is available to external Javascript as an API.
@@ -251,7 +251,7 @@
haoyi-mbp:temp haoyi$ du -h target/scala-2.11/example-opt.js
144K target/scala-2.11/example-opt.js
- @p
+ @p
144 Kilobytes! Better. Not great, though! In general, Scala.js does not produce tiny executables, although the output size of the compiled executables is dropping all the time. If you look inside that file, you'll see all of the long identifiers have been replaced by short ones by the Google Closure Compiler.
@hl.javascript("""
@@ -269,7 +269,7 @@
Even the fully-optimized version of our toy Scala.js app are pretty large. There are some factors that mitigate the large size of these executables:
@ul
- @li
+ @li
A large portion of this 144k is the Scala standard library, and so the size of the compiled blob does not grow that fast as your program grows. For example, while this ~50 line application is 144k, a much larger ~2000 line application is only 288k.
@li
This size is pre-@a("gzip", href:="http://en.wikipedia.org/wiki/Gzip"), and most webservers serve their contents compressed via gzip to reduce the download size. Gzip cuts the actual download size down to 43k, which is more acceptable.
@@ -293,17 +293,17 @@
If you've made it this far, you've downloaded, made modifications to, and published a toy Scala.js application. At the same time, we've gone over many of the key concepts in the Scala.js development process:
@ul
- @li
+ @li
Getting a Scala.js application
@li
Building it and seeing it work in the browser
- @li
+ @li
Made modifications to it to see it update
@li
Examined the source code to try and understand what it's doing
@li
Examined the output code, at two levels of optimization, to see how the Scala.js compiler works
- @li
+ @li
Packaged the application in a form that can be easily published online
@p
@@ -323,4 +323,4 @@
Try publishing the output code somewhere. You only need @code{example-opt.js} and @code{index-opt.html}; try putting them somewhere online where the world can see it.
@p
- When you're done poking around our toy web application, read on to the next chapter, where we will explore making something more meaty using the Scala.js toolchain!
+ When you're done poking around our toy web application, read on to the next chapter, where we will explore making something more meaty using the Scala.js toolchain!
diff --git a/book/src/main/scalatex/book/handson/PublishingModules.scalatex b/book/src/main/scalatex/book/handson/PublishingModules.scalatex
index b329966..cad4647 100644
--- a/book/src/main/scalatex/book/handson/PublishingModules.scalatex
+++ b/book/src/main/scalatex/book/handson/PublishingModules.scalatex
@@ -50,7 +50,7 @@
@p
In @code{Simple.scala} we have the shared, cross-platform API of our library: a single @hl.scala{object} with a single method @hl.scala{def} which does what we want, which can then be used in either Scala.js or Scala-JVM. In general, you can put as much shared logic here as you want: classes, objects, methods, anything that can run on both Javascript and on the JVM. We're chopping off the last 5 characters (the milliseconds) to keep the formatted dates slightly less verbose.
-
+
@p
However, when it comes to actually formatting the date, we have a problem: Javascript and Java provide different utilities for formatting dates! They both let you format them, but they provide different APIs. Thus, to do the formatting of each individual date, we call out to the @hl.scala{Platform.format} function, which is implemented twice: once in @code{js/} and once in @code{jvm/}:
@@ -62,14 +62,14 @@
@hl.ref("examples/crossBuilds/simple/jvm/src/main/scala/simple/Platform.scala")
@p
- In the @code{js/} version, we are using the Javascript @hl.javascript{Date} object to take the millis and do what we want. In the @code{jvm/} version, we instead use @hl.scala{java.text.SimpleDateFormat} with a custom formatter (The syntax is defined @a("here", href:="http://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html")).
-
+ In the @code{js/} version, we are using the Javascript @hl.javascript{Date} object to take the millis and do what we want. In the @code{jvm/} version, we instead use @hl.scala{java.text.SimpleDateFormat} with a custom formatter (The syntax is defined @a("here", href:="http://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html")).
+
@p
Again, you can put as much platform-specific logic in these files as you want, to account for differences in the available APIs. Maybe you want to use @hl.scala{js.JSON.parse} for parsing JSON blobs in @code{js/}, but @hl.scala{Jackson} or @hl.scala{GSON} for parsing them in @code{jvm/}.
@p
Lastly, you'll also have noticed the two identical @hl.scala{main} methods in the platform-specific code. This is an implementation detail around the fact that Scala.js picks up the main method differently from Scala-JVM, using @hl.scala{js.JSApp} instead of looking for a @hl.scala{main(args: Array[String]): Unit} method. These two main methods allow us to test our implementations
@sect{Running the Module}
- @hl.bash
+ @hl.bash
> ; js/run; jvm/run
[info] Running simple.Platform
Running on JS! 1
@@ -86,15 +86,15 @@
As you can see, both runs printed the same results, modulo three things:
@ul
- @li
- The "Running on XXX!" statement which shows us we're actually running on two platforms.
+ @li
+ The "Running on XXX!" statement which shows us we're actually running on two platforms.
@li
The other hint is the time taken: the second run is instant while the first takes three seconds! This is because by default we run on @a("Rhino", href:="https://developer.mozilla.org/en-US/docs/Mozilla/Projects/Rhino"), which is a simple interpreter hundreds of times slower than running code natively on the JVM.
@li
In Scala-JVM the double 1.0 is printed as @code{1.0}, while in Scala.js it's printed as @code{1}. This is one of a small number of differences between Scala.js and Scala-JVM, and verifies that we are indeed running on both platforms!
@p
- You've by this point set up a basic cross-building Scala.js/Scala-JVM project!
+ You've by this point set up a basic cross-building Scala.js/Scala-JVM project!
@hr
@@ -106,7 +106,7 @@
Flesh it out! Currently this module only does a single, trivial thing. If you've done any web development before, I'm sure you can find some code snippet, function or algorithm that you'd like to share between client and server. Try implementing it in the @code{shared/} folder to be usable in both Scala.js and Scala-JVM
@li
Publish it! Both @code{sbt publishLocal} and @code{sbt publishSigned} work on this module, for publishing either locally, Maven Central via Sonatype, or Bintray. Running the command bare should be sufficient to publish both the @code{js} or @code{jvm} projects, or you can also specify which one e.g. @code{jvm/publishLocal} to publish only one subproject.
-
+
@p
This @code{jvm} project works identically to any other Scala-JVM project, and the @code{js} project works identically to the Command Line API described earlier. Thus you can do things like @code{fastOptStage::run} to run the code on Node.js, setting @hl.scala{requiresDOM := true}, run @code{fullOptStage::run} to run the code with full, aggressive optimizations. And of course, things that work in both Scala.js and Scala-JVM can be run on both, basic commands such as code{run} or @code{test}.
@@ -174,7 +174,7 @@
[success] Total time: 0 s, completed Nov 8, 2014 7:42:39 PM
@p
- And you'll see that we've run our unit tests twice: once on Scala.js in Rhino, and once on Scala-JVM! As expected, the first run in Rhino took much longer (4 seconds!) than the second, as Rhino is much slower than running code directly on the JVM. You can configure the Scala.js run to run in Node.js or PhantomJS, as well as running under different optimization levels.
+ And you'll see that we've run our unit tests twice: once on Scala.js in Rhino, and once on Scala-JVM! As expected, the first run in Rhino took much longer (4 seconds!) than the second, as Rhino is much slower than running code directly on the JVM. You can configure the Scala.js run to run in Node.js or PhantomJS, as well as running under different optimization levels.
@hr
diff --git a/book/src/main/scalatex/book/handson/WebPage.scalatex b/book/src/main/scalatex/book/handson/WebPage.scalatex
index c3f66a4..5415ec7 100644
--- a/book/src/main/scalatex/book/handson/WebPage.scalatex
+++ b/book/src/main/scalatex/book/handson/WebPage.scalatex
@@ -41,7 +41,7 @@
@p
With that, the above snippet of code re-written using Scalatags looks as follows:
- @div(cls:="pure-g")
+ @div(cls:="pure-g")
@div(cls:="pure-u-1 pure-u-md-13-24")
@hl.ref("examples/demos/src/main/scala/webpage/HelloWorld1.scala")
@@ -56,7 +56,7 @@
The Scalatags github page has @a("comprehensive documentation", href:="https://github.com/lihaoyi/scalatags#hello-world") on how to express all manner of HTML fragments using Scalatags, so anyone who's familiar with how HTML works can quickly get up to speed. Instead of a detailed listing, we'll walk through some interactive examples to show Scalatags in action!
@sect{User Input}
- @div(cls:="pure-g")
+ @div(cls:="pure-g")
@div(cls:="pure-u-1 pure-u-md-13-24")
@hl.ref("examples/demos/src/main/scala/webpage/Inputs.scala", "val box")
@@ -76,7 +76,7 @@
@p
To begin with, let's define our list of items: Fruits!
-
+
@hl.ref("examples/demos/src/main/scala/webpage/Search0.scala", "val listings", "def")
@p
@@ -90,7 +90,7 @@
@p
Lastly, we just need to define the input box and output-container (as we did earlier), set the @hl.scala{onkeyup} event handler, and place it in a larger fragment, and then into our target:
- @div(cls:="pure-g")
+ @div(cls:="pure-g")
@div(cls:="pure-u-1 pure-u-md-13-24")
@hl.ref("examples/demos/src/main/scala/webpage/Search0.scala", "val output")
@@ -101,7 +101,7 @@
@p
And there you have it! A working search box. This is a relatively self-contained example: all the items its searching are available locally, no Ajax calls, and there's no fancy handling of the searched items. If we want to, for example, highlght the matched section of each fruit's name, we can modify the @hl.scala{def renderListings} call to do so:
- @div(cls:="pure-g")
+ @div(cls:="pure-g")
@div(cls:="pure-u-1 pure-u-md-13-24")
@hl.ref("examples/demos/src/main/scala/webpage/Search1.scala", "def renderListings", "lazy val")
@@ -134,7 +134,7 @@
One half of the web application faces forwards towards the user, managing and rendering HTML or Canvas for the user to view and interact with. Another half faces backwards, talking to various web-services or databases which turn the application from a standalone-widget into part of a greater whole. We've already seen how to make the front half, let's now talk about working with the back half.
@sect{Raw Javascript}
- @div(cls:="pure-g")
+ @div(cls:="pure-g")
@div(cls:="pure-u-1 pure-u-md-13-24")
@hl.ref("examples/demos/src/main/scala/webpage/Weather0.scala", "val xhr")
@@ -142,12 +142,12 @@
@div(id:="div6")
@script("Weather0().main(document.getElementById('div6'))")
@p
- The above snippet of code uses the raw Javascript Ajax API in order to make a request to @a("openweathermap.org", href:="http://openweathermap.org/"), to get the weather data for the city of Singapore as a JSON blob. The part of the API that we'll be using is documented @a("here", href:="http://openweathermap.org/current"). For now, we're unceremoniously dumping it in a @hl.scala{pre} so you can see the raw response data.
+ The above snippet of code uses the raw Javascript Ajax API in order to make a request to @a("openweathermap.org", href:="http://openweathermap.org/"), to get the weather data for the city of Singapore as a JSON blob. The part of the API that we'll be using is documented @a("here", href:="http://openweathermap.org/current"). For now, we're unceremoniously dumping it in a @hl.scala{pre} so you can see the raw response data.
@p
As you can see, using the raw Javascript API to make the Ajax call looks almost identical to actually doing this in Javascript, shown below:
- @div(cls:="pure-g")
+ @div(cls:="pure-g")
@div(cls:="pure-u-1 pure-u-md-13-24")
@hl.ref("examples/demos/src/main/resources/webpage/weather.js", "var xhr")
@@ -160,32 +160,32 @@
The primary syntactic differences are:
@ul
- @li
+ @li
@hl.scala{val}s for immutable data v.s. mutable @hl.javascript{var}s.
@li
@hl.scala("=>") v.s. @hl.javascript{function} to define the callback.
@li
- Scalatags' @hl.scala{pre} v.s. @hl.javascript{document.createElement}
+ Scalatags' @hl.scala{pre} v.s. @hl.javascript{document.createElement}
@p
Overall, they're pretty close, which is a common theme in Scala.js: using Javascript APIs in Scala.js is often as seamless and easy as using them in Javascript itself, and it often looks almost identical.
@sect{dom.extensions}
@p
- Although the Javascript XMLHttpRequest API is workable, it's kind of awkward and clunky compared to what you're used to in Scala. We create a half-baked object, set some magic properties, and call a magic function, which all has to be done in the correct order or it won't work.
+ Although the Javascript XMLHttpRequest API is workable, it's kind of awkward and clunky compared to what you're used to in Scala. We create a half-baked object, set some magic properties, and call a magic function, which all has to be done in the correct order or it won't work.
@p
With Scala.js, we provide a simpler API that is more clearly functional. First, you need to import some things into scope:
@hl.ref("examples/demos/src/main/scala/webpage/Weather1.scala", "import dom", "val url =")
-
+
@p
The first import brings in Scala adapters to several DOM APIs, which allow you to use them more idiomatically from Scala. The second brings in an implicit @hl.scala{ExecutionContext} that we'll need to run our asynchronous operations.
@p
Then we need the code itself:
- @div(cls:="pure-g")
+ @div(cls:="pure-g")
@div(cls:="pure-u-1 pure-u-md-13-24")
@hl.ref("examples/demos/src/main/scala/webpage/Weather1.scala", "val url")
@@ -203,7 +203,7 @@
@p
First, let's make the call prettyprint the document, so at least we can see what it contains:
- @div(cls:="pure-g")
+ @div(cls:="pure-g")
@div(cls:="pure-u-1 pure-u-md-13-24")
@hl.ref("examples/demos/src/main/scala/webpage/Weather2.scala", "Ajax.get")
@@ -217,7 +217,7 @@
@p
Now that we've pretty-printed it, we can immediately see what data it contains and which part of the data we want. Let's change the previous example's @hl.scala{onSuccess} call to extract the @hl.scala{weather}, @hl.scala{temp} and @hl.scala{humidity} and put them in a nice, human-friendly format for us to enjoy:
- @div(cls:="pure-g")
+ @div(cls:="pure-g")
@div(cls:="pure-u-1 pure-u-md-13-24")
@hl.ref("examples/demos/src/main/scala/webpage/Weather3.scala", "Ajax.get")
@@ -226,7 +226,7 @@
@script("Weather3().main(document.getElementById('div10'))")
@p
- First we parse the incoming response, extract a bunch of values from it, and then stick it in a Scalatags fragment for us to see. Note how we can use the names of the attributes e.g. @hl.scala{json.name} even though @hl.scala{name} is a dynamic property which you can't be sure exists: this is because @hl.scala{json} is of type @hl.scala{js.Dynamic}, which allows us to refer to arbitrary parameters and methods on the underlying object without type-checking.
+ First we parse the incoming response, extract a bunch of values from it, and then stick it in a Scalatags fragment for us to see. Note how we can use the names of the attributes e.g. @hl.scala{json.name} even though @hl.scala{name} is a dynamic property which you can't be sure exists: this is because @hl.scala{json} is of type @hl.scala{js.Dynamic}, which allows us to refer to arbitrary parameters and methods on the underlying object without type-checking.
@p
Calls on @hl.scala{js.Dynamic} resolve directly to javascript property/method references, and will fail at run-time with an exception if used wrongly. This is also why we need to call @hl.scala{.toString} or @hl.scala{.asInstanceOf}on the values before use: without these casts, the compiler can't be sure what kind of value is underneath the @hl.scala{js.Dynamic} type, and so we have to provide it the guarantee that it is what it needs.
@@ -254,12 +254,12 @@
Here is the meat and potatoes of this program: every time it gets called with an array of weather-data, we iterate over the cities in that array. It then does a similar sort of data-extraction that we did earlier, putting the results into the @hl.scala{output} div we defined above, including highlighting.
@div(id:="div11")
- @script("WeatherSearch().main(document.getElementById('div11'))")
+ @script("WeatherSearch().main(document.getElementById('div11'))")
@p
And that's the working example! Try searching for cities like "Singapore" or "New York" or "San Francisco" and watch as the search narrows as you enter more characters into the text box. Note that the OpenWeatherMap API limits ambiguous searches to about a dozen results, so if a city doesn't turn up in a partial-search, try entering more characters to narrow it down.
-@sect{Interactive Web Pages Recap}
+@sect{Interactive Web Pages Recap}
@p
In this chapter, we've explored the basics of how you can use Scala.js to build interactive web pages. The two main contributions are using Scalatags to render HTML in a concise, safe way, and making Ajax calls to external web services. We combined these two capabilities in a small weather-search app that let a user interactively search for the weather in different cities around the world.
@p
@@ -272,6 +272,6 @@
Using @hl.scala{new dom.XMLHttpRequest} to make web requests feels just like the Javascript code to do so
@li
Using @hl.scala{Ajax.get(...)} and working with the resultant : @hl.scala{Future} feels a lot cleaner than directly using the Javascript API
-
+
@p
You're at this point reasonably pro