summaryrefslogtreecommitdiff
path: root/book/src/main/scalatex/book/handson/WebPage.scalatex
diff options
context:
space:
mode:
Diffstat (limited to 'book/src/main/scalatex/book/handson/WebPage.scalatex')
-rw-r--r--book/src/main/scalatex/book/handson/WebPage.scalatex36
1 files changed, 19 insertions, 17 deletions
diff --git a/book/src/main/scalatex/book/handson/WebPage.scalatex b/book/src/main/scalatex/book/handson/WebPage.scalatex
index 118dd0c..df60176 100644
--- a/book/src/main/scalatex/book/handson/WebPage.scalatex
+++ b/book/src/main/scalatex/book/handson/WebPage.scalatex
@@ -6,6 +6,8 @@
@p
At this point, you are already competent at using Scala.js to make basic, self-contained canvas applications. In this chapter, we will cover how to use Scala.js to build the sort of interactive-web-pages that make up the bulk of the modern-day internet. We'll cover how to use powerful libraries that turn front-end development form the typical fragile-mess into a structured, robust piece of software.
+@val webpage = wd/'examples/'demos/'src/'main/'scala/'webpage
+
@sect{Hello World: HTML}
@p
@@ -13,7 +15,7 @@
@split
@more
- @hl.ref("examples/demos/src/main/scala/webpage/HelloWorld0.scala")
+ @hl.ref(webpage/"HelloWorld0.scala")
@less
@BookData.example(div, "HelloWorld0().main")
@@ -37,14 +39,14 @@
@p
@lnk("Scalatags", "https://github.com/lihaoyi/scalatags") is a cross-platform Scala.js/Scala-JVM library that is designed to generate HTML. To use Scalatags, you need to add it as a dependency to your Scala.js SBT project, in the @code{build.sbt} file:
- @hl.ref("examples/demos/build.sbt", "com.scalatags", "")
+ @hl.ref(wd/'examples/'demos/"build.sbt", "com.scalatags", "")
@p
With that, the above snippet of code re-written using Scalatags looks as follows:
@split
@more
- @hl.ref("examples/demos/src/main/scala/webpage/HelloWorld1.scala")
+ @hl.ref(webpage/"HelloWorld1.scala")
@less
@BookData.example(div, "HelloWorld1().main")
@@ -58,7 +60,7 @@
@sect{User Input}
@split
@more
- @hl.ref("examples/demos/src/main/scala/webpage/Inputs.scala", "val box")
+ @hl.ref(webpage/"Inputs.scala", "val box")
@less
@BookData.example(div(height:="150px"), "Inputs().main")
@@ -76,12 +78,12 @@
@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")
+ @hl.ref(webpage/"Search0.scala", "val listings", "def")
@p
Next, let's think about how we want to render these fruits. One natural way would be as a list, which in HTML is represented by a @hl.html{<ul>} with @hl.html{<li>}s inside of it if we wanted the list to be unordered. We'll make it a @hl.scala{def}, because we know up-front we're going to need to re-render this listing as the search query changes. Lastly, we know we want 1 list item for each fruit, but only if the fruit starts with the search query.
- @hl.ref("examples/demos/src/main/scala/webpage/Search0.scala", "def renderListings", "lazy val")
+ @hl.ref(webpage/"Search0.scala", "def renderListings", "lazy val")
@p
Using a @hl.scala{for}-loop with a filter inside the Scalatags fragment is just normal Scala, since you can nest arbitrary Scala expressions inside a Scalatags snippet. In this case, we're converting both the fruit and the search query to lower case so we can compare them case-insensitively.
@@ -91,7 +93,7 @@
@split
@more
- @hl.ref("examples/demos/src/main/scala/webpage/Search0.scala", "val output")
+ @hl.ref(webpage/"Search0.scala", "val output")
@less
@BookData.example(div, "Search0().main")
@@ -101,7 +103,7 @@
@split
@more
- @hl.ref("examples/demos/src/main/scala/webpage/Search1.scala", "def renderListings", "lazy val")
+ @hl.ref(webpage/"Search1.scala", "def renderListings", "lazy val")
@less
@BookData.example(div, "Search1().main")
@@ -133,7 +135,7 @@
@sect{Raw Javascript}
@split
@more
- @hl.ref("examples/demos/src/main/scala/webpage/Weather0.scala", "val xhr")
+ @hl.ref(webpage/"Weather0.scala", "val xhr")
@less
@BookData.example(div(height:="400px"), "Weather0().main")
@@ -145,7 +147,7 @@
@split
@more
- @hl.ref("examples/demos/src/main/resources/webpage/weather.js", "var xhr")
+ @hl.ref(webpage/"weather.js", "var xhr")
@less
@BookData.example(div, "WeatherJs")
@@ -171,7 +173,7 @@
@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 =")
+ @hl.ref(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{scala.concurrent.ExecutionContext} that we'll need to run our asynchronous operations.
@@ -181,7 +183,7 @@
@split
@more
- @hl.ref("examples/demos/src/main/scala/webpage/Weather1.scala", "val url")
+ @hl.ref(webpage/"Weather1.scala", "val url")
@less
@BookData.example(div(height:="400px", overflow:="scroll"), "Weather1().main")
@@ -198,7 +200,7 @@
@split
@more
- @hl.ref("examples/demos/src/main/scala/webpage/Weather2.scala", "Ajax.get")
+ @hl.ref(webpage/"Weather2.scala", "Ajax.get")
@less
@BookData.example(div(height:="400px"), "Weather2().main")
@@ -211,7 +213,7 @@
@split
@more
- @hl.ref("examples/demos/src/main/scala/webpage/Weather3.scala", "Ajax.get")
+ @hl.ref(webpage/"Weather3.scala", "Ajax.get")
@less
@BookData.example(div(height:="400px", overflow:="scroll"), "Weather3().main")
@@ -226,12 +228,12 @@
@p
At this point we've made a small app that allows us to search from a pre-populated list of words, as well as a small app that lets us query a remote web-service to find the weather in Singapore. The natural thing to do is to put these things together to make a app that will let us search from a list of countries and query the weather in any country we desire. Let's start!
- @hl.ref("examples/demos/src/main/scala/webpage/WeatherSearch.scala", "lazy val box", "def fetchWeather")
+ @hl.ref(webpage/"WeatherSearch.scala", "lazy val box", "def fetchWeather")
@p
This sets up the basics: an input box, an output div, and sets an @hl.scala{onkeyup} that fetches the weather data each time you hit a key. It then renders all these components and sticks them into the @hl.scala{target} div. This is basically the same stuff we saw in the early examples, with minor tweaks e.g. adding a @hl.scala{maxHeight} and @hl.scala{overflowY:="scroll"} to the @hl.scala{output} box in case the output is too large. Whenever we enter something in the box, we call the function @hl.scala{fetchWeather}, which is defined as:
- @hl.ref("examples/demos/src/main/scala/webpage/WeatherSearch.scala", "def fetchWeather", "def showResults")
+ @hl.ref(webpage/"WeatherSearch.scala", "def fetchWeather", "def showResults")
@p
This is where the actual data fetching happens. It's relatively straightforward: we make an @hl.scala{Ajax.get} request, @hl.scala{JSON.parse} the response, and feed it into the callback function. We're using a slightly different API from earlier: we now have the @hl.scala{"type=like"} flag, which is documented in the @lnk("OpenWeatherMap API docs", "http://openweathermap.org/current#other") to return multiple results for each city whose name matches your query.
@@ -239,7 +241,7 @@
@p
Notably, before we re-render the results, we check whether the @hl.scala{query} that was passed in is the same value that's in the @hl.scala{box}. This is to prevent a particularly slow ajax call from finishing out-of-order, potentially stomping over the results of more recent searches. We also check whether the @hl.scala{.list: js.Dynamic} property we want is an instance of @hl.scala{js.Array}: if it isn't, it means we don't have any results to show, and we can skip the whole render-output step.
- @hl.ref("examples/demos/src/main/scala/webpage/WeatherSearch.scala", "def showResults")
+ @hl.ref(webpage/"WeatherSearch.scala", "def showResults")
@p
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.