diff options
Diffstat (limited to 'book/src/main/scalatex/book/handson/WebPage.scalatex')
-rw-r--r-- | book/src/main/scalatex/book/handson/WebPage.scalatex | 36 |
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. |