summaryrefslogtreecommitdiff
path: root/book/src/main/scalatex/handson/WebPage.scalatex
diff options
context:
space:
mode:
Diffstat (limited to 'book/src/main/scalatex/handson/WebPage.scalatex')
-rw-r--r--book/src/main/scalatex/handson/WebPage.scalatex36
1 files changed, 18 insertions, 18 deletions
diff --git a/book/src/main/scalatex/handson/WebPage.scalatex b/book/src/main/scalatex/handson/WebPage.scalatex
index 13b5b20..cc232f2 100644
--- a/book/src/main/scalatex/handson/WebPage.scalatex
+++ b/book/src/main/scalatex/handson/WebPage.scalatex
@@ -11,24 +11,24 @@
@sect{Hello World: HTML}
@p
- The most basic way of building interactive web pages using Scala.js is to use the Javascript APIs to blat HTML strings directly into some container @hl.html{<div>} or @hl.html{<body>}. This approach works, as the following code snippet demonstrates:
+ The most basic way of building interactive web pages using Scala.js is to use the Javascript APIs to blat HTML strings directly into some container @hl.xml{<div>} or @hl.xml{<body>}. This approach works, as the following code snippet demonstrates:
@split
@more
@hl.ref(webpage/"HelloWorld0.scala")
@less
- @BookData.example(div, "webpage.HelloWorld0().main")
+ @book.BookData.example(div, "webpage.HelloWorld0().main")
@p
- Remember that we're now requiring a @hl.scala{html.Div} instead of a @hl.scala{html.Canvas} to be passed in when the Javascript calls @hl.javascript{webpage.HelloWorld0().main(...)}. If you're coming to this point from the previous chapter, you'll need to update the on-page Javascript's @hl.javascript{document.getElementById} to pick a @hl.html{<div>} rather than the @hl.html{<canvas>} we were using in the previous chapter.
+ Remember that we're now requiring a @hl.scala{html.Div} instead of a @hl.scala{html.Canvas} to be passed in when the Javascript calls @hl.js{webpage.HelloWorld0().main(...)}. If you're coming to this point from the previous chapter, you'll need to update the on-page Javascript's @hl.js{document.getElementById} to pick a @hl.xml{<div>} rather than the @hl.xml{<canvas>} we were using in the previous chapter.
@p
This approach works, as the above example shows, but has a couple of disadvantages:
@ul
@li
- It is untyped: it is easy to accidentally mistype something, and result in malformed HTML. A typo such as @hl.html{<dvi>} would go un-noticed at build-time. Depending on where the typo happens, it could go un-noticed until the application is deployed, causing subtle bugs that only get resolved much later.
+ It is untyped: it is easy to accidentally mistype something, and result in malformed HTML. A typo such as @hl.xml{<dvi>} would go un-noticed at build-time. Depending on where the typo happens, it could go un-noticed until the application is deployed, causing subtle bugs that only get resolved much later.
@li
It is insecure: @lnk("Cross-site Scripting", "http://en.wikipedia.org/wiki/Cross-site_scripting") is a real thing, and it is easy to forget to escape the values you are putting into your HTML strings. Above they're constants like @hl.scala{"dog"}, but if they're user-defined, you may not notice there is a problem until something like @hl.scala{"<script>...</script>"} sneaks through and your users' accounts & data is compromised.
@@ -49,7 +49,7 @@
@hl.ref(webpage/"HelloWorld1.scala")
@less
- @BookData.example(div, "webpage.HelloWorld1().main")
+ @book.BookData.example(div, "webpage.HelloWorld1().main")
@p
Scalatags has some nice advantages over plain HTML: it's type-safe, so typos like @hl.scala{dvi} get caught at compile-time. It's also secure, such that you don't need to worry about script-tags in strings or similar. The @lnk("Scalatags Readme", "https://github.com/lihaoyi/scalatags#scalatags") elaborates on these points and other advantages. As you can see, it takes just 1 import at the top of the file to bring it in scope, and then you can use all of Scalatags' functionality.
@@ -63,7 +63,7 @@
@hl.ref(webpage/"Inputs.scala", "val box")
@less
- @BookData.example(div(height:="150px"), "webpage.Inputs().main")
+ @example(div(height:="150px"), "webpage.Inputs().main")
@p
In Scalatags, you build up fragments of type @hl.scala{Frag} using functions like @hl.scala{div}, @hl.scala{h1}, etc., and call @hl.scala{.render} on it to turn it into a real @lnk.dom.Element. Different fragments render to different things: e.g. @hl.scala{input.render} gives you a @lnk.dom.html.Input, @hl.scala{span.render} gives you a @lnk.dom.html.Span. You can then access the properties of these elements: adding callbacks, checking their value, anything you want.
@@ -81,7 +81,7 @@
@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.
+ 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.xml{<ul>} with @hl.xml{<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(webpage/"Search0.scala", "def renderListings", "lazy val")
@@ -96,7 +96,7 @@
@hl.ref(webpage/"Search0.scala", "val output")
@less
- @BookData.example(div, "webpage.Search0().main")
+ @example(div, "webpage.Search0().main")
@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, highlight the matched section of each fruit's name, we can modify the @hl.scala{def renderListings} call to do so:
@@ -106,7 +106,7 @@
@hl.ref(webpage/"Search1.scala", "def renderListings", "lazy val")
@less
- @BookData.example(div, "webpage.Search1().main")
+ @example(div, "webpage.Search1().main")
@p
Here, instead of sticking the name of the matched fruits directly into the @hl.scala{li}, we instead first split off the part which matches the query, and then highlght the first section yellow. Easy!
@@ -138,7 +138,7 @@
@hl.ref(webpage/"Weather0.scala", "val xhr")
@less
- @BookData.example(div(height:="400px"), "webpage.Weather0().main")
+ @example(div(height:="400px"), "webpage.Weather0().main")
@p
The above snippet of code uses the raw Javascript Ajax API in order to make a request to @lnk("openweathermap.org", "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 @lnk("here", "http://openweathermap.org/current"), and if you're interested you can read all about the various options that they provide. For now, we're unceremoniously dumping it in a @hl.scala{pre} so you can see the raw response data.
@@ -150,18 +150,18 @@
@hl.ref(wd/'examples/'demos/'src/'main/'resources/'webpage/"weather.js", "var xhr")
@less
- @BookData.example(div, "WeatherJs")
+ @example(div, "WeatherJs")
@p
The primary syntactic differences are:
@ul
@li
- @hl.scala{val}s for immutable data v.s. mutable @hl.javascript{var}s.
+ @hl.scala{val}s for immutable data v.s. mutable @hl.js{var}s.
@li
- @hl.scala("=>") v.s. @hl.javascript{function} to define the callback.
+ @hl.scala("=>") v.s. @hl.js{function} to define the callback.
@li
- Scalatags' @hl.scala{pre} v.s. @hl.javascript{document.createElement("pre")}
+ Scalatags' @hl.scala{pre} v.s. @hl.js{document.createElement("pre")}
@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.
@@ -186,7 +186,7 @@
@hl.ref(webpage/"Weather1.scala", "val url")
@less
- @BookData.example(div(height:="400px", overflow:="scroll"), "webpage.Weather1().main")
+ @example(div(height:="400px", overflow:="scroll"), "webpage.Weather1().main")
@p
A single call to @hl.scala{Ajax.get(...)}, with the URL, and we receive a @hl.scala{scala.concurrent.Future} that we can use to get access to the result when ready. Here we're just using it's @hl.scala{onSuccess}, but we could use it in a for-comprehension, with @lnk("Scala Async", "https://github.com/scala/async"), or however else we can use normal @hl.scala{Future}s
@@ -203,7 +203,7 @@
@hl.ref(webpage/"Weather2.scala", "Ajax.get")
@less
- @BookData.example(div(height:="400px"), "webpage.Weather2().main")
+ @example(div(height:="400px"), "webpage.Weather2().main")
@p
We do this by taking @hl.scala{xhr.responseText} and putting it through both @hl.scala{JSON.parse} and @hl.scala{JSON.stringify}, passing in a @hl.scala{space} argument to tell @hl.scala{JSON.stringify} to spread it out nicely.
@@ -216,7 +216,7 @@
@hl.ref(webpage/"Weather3.scala", "Ajax.get")
@less
- @BookData.example(div(height:="400px", overflow:="scroll"), "webpage.Weather3().main")
+ @example(div(height:="400px", overflow:="scroll"), "webpage.Weather3().main")
@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.
@@ -246,7 +246,7 @@
@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.
- @BookData.example(div, "webpage.WeatherSearch().main")
+ @example(div, "webpage.WeatherSearch().main")
@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.