diff --git a/02-guides/cmd/index.html b/02-guides/cmd/index.html index b63f88fb..dd34e55a 100644 --- a/02-guides/cmd/index.html +++ b/02-guides/cmd/index.html @@ -153,19 +153,19 @@
<div id="mdoc-html-run1" data-mdoc-js></div>
+But commands can also return values in the form of messages. The Random
command looks like this:
Random.double[IO]
- <div id="mdoc-html-run2" data-mdoc-js></div>
+...and produces an instance of RandomValue
, but this leads to a problem since RandomValue
is almost certainly not your app's Msg
type, and so we must map over the result:
enum MyMsg:
case MyRandom(d: Double) extends MyMsg
Random.double[IO].map(next => MyMsg.MyRandom(next.value))
- <div id="mdoc-html-run3" data-mdoc-js></div>
+These are simple examples, but there are much more complicated uses for commands. One great use of commands is for making HTTP requests where the response is decoded into a Msg
.
- <script type="text/javascript" src="cmd.md.js" defer></script>
- <script type="text/javascript" src="mdoc.js" defer></script>
<div id="mdoc-html-run2" data-mdoc-js></div>
+Http
Please see Networking for details.
@@ -184,7 +184,7 @@<div id="mdoc-html-run3" data-mdoc-js></div>
+LocalStorage
A series of commands that mirror the localstorage interface.
@@ -217,7 +217,7 @@<div id="mdoc-html-run4" data-mdoc-js></div>
+Logger
Allows you to log to your browsers JavaScript console:
@@ -225,13 +225,13 @@<div id="mdoc-html-run5" data-mdoc-js></div>
+If you're app is doing a lot of regular work, you can cut down the noise with the 'once' versions:
import tyrian.cmds.*
val cmd: Cmd[IO, Msg] =
Logger.debugOnce("Log this exact message only once!")
- <div id="mdoc-html-run6" data-mdoc-js></div>
+Random
As you might expect, Random
produces random values! Random works slightly differently from other commands, in that it doesn't except a conversion function to turn the result into a message. You do that by mapping over it.
<div id="mdoc-html-run7" data-mdoc-js></div>
+Cmd
+ Sub
goodiesThese tools make use of a combination of commands and subscriptions to achieve a result. Note that unlike in the next section, these entries share nothing apart from, say, a key value, i.e. there is no common state to manage or store in a model.
@@ -274,8 +274,8 @@WebSocket
- Allows you to send and receive data to/from a socket server (see Networking).TyrianIndigoBridge
- Allows your Tyrian app to communicates with embedded Indigo games.
- <script type="text/javascript" src="goodies.md.js" defer></script>
- <script type="text/javascript" src="mdoc.js" defer></script><div id="mdoc-html-run0" data-mdoc-js></div>
+Lets go through it...
TyrianIOApp
/ TyrianZIOApp
type Model = Int
- <div id="mdoc-html-run1" data-mdoc-js></div>
+Our app is a counter, so we need a number we can increment and decrement. In this super simple example, an Int
is all that we need for our whole model. Normally you'd probably have a case class
or something instead. To make it fit nicely, we've allocated our Int
to a Model
type alias.
The version in the examples uses an opaque type, but here we've reduced it to a type alias.
To use our model, we're going to have to initialize it!
@@ -178,7 +178,7 @@ def init(flags: Map[String, String]): (Model, Cmd[IO, Msg]) =
(0, Cmd.None)
- <div id="mdoc-html-run3" data-mdoc-js></div>
+There's a few things going on here, the only bit we really care about here is the 0
because that is going to be the starting value of our 'model'.
Some of the other things you can see here:
<div id="mdoc-html-run4" data-mdoc-js></div>
+Here we make a div, add a -
button, the another div containing the count (i.e. the model) as plain text, and finally another +
button. If you're familiar with HTML this should all look pretty familiar.
If you wanted to add an id
attribute to the div, you would do so like this:
def view(model: Model): Html[Msg] =
@@ -204,7 +204,7 @@ div
(model.toString),
button("+")
)
- <div id="mdoc-html-run5" data-mdoc-js></div>
+Of course a button isn't much use unless it does something, and what we can do is emit an event, called a message, when the button is clicked. For that we need to declare our message type which we'll do as a simple enum that represents the two actions we want to perform:
enum Msg:
case Increment, Decrement
@@ -215,7 +215,7 @@ <div id="mdoc-html-run7" data-mdoc-js></div>
+Note the return type of view isHtml[Msg]
. This is because unlike normal JavaScript, theonClick
is not directly instigating a normal callback, the HTML elements are mapped through and produce messages as values that are passed back to Tyrian.
def update(model: Model): Msg => (Model, Cmd[IO, Msg]) =
case Msg.Increment => (model + 1, Cmd.None)
case Msg.Decrement => (model - 1, Cmd.None)
- <div id="mdoc-html-run8" data-mdoc-js></div>
+Recall that our 'model' is just a type alias for an Int
, so all we do is match on the Msg
enum type, and either increment or decrement the model - done!
Subscriptions are part of the standard requirements, but this example doesn't use them for anything. They allow you to "subscribe" to processes that emit events over time. - <script type="text/javascript" src="guided-tour.md.js" defer></script> - <script type="text/javascript" src="mdoc.js" defer></script>
+ +span(`class` := "green-box")(
p("This is some text.")
)
- <div id="mdoc-html-run1" data-mdoc-js></div>
+You can omit the attributes and the syntax is valid:
span(
p("This is some text.")
)
- <div id="mdoc-html-run2" data-mdoc-js></div>
+Note that plain text can be declare as text
or just omitted, in other words these are equivalent:
p("some text")
p(text("some text"))
- <div id="mdoc-html-run3" data-mdoc-js></div>
+To distinguish them from similarly named tags (e.g. the title
attribute and the title
tag...), attributes are declared as attribute-name := attribute-value
, e.g.:
id := "my-container"
- <div id="mdoc-html-run4" data-mdoc-js></div>
+Some HTML attributes / properties use Scala reserved words, and so have a variety of encodings to suit all tastes. For example class
is a Scala reserved word and you cannot use it directly, but you can use any of these instead:
`class`
cls
@@ -167,7 +167,7 @@ p(style(CSS.`font-weight`("bold")))("Hello")
- <div id="mdoc-html-run5" data-mdoc-js></div>
+Sometime you might want to optionally render a tag, or not. To help with this, you can use the orEmpty
extension method:
You can also pull in SVG tags and attributes using:
import tyrian.SVG.*
- <div id="mdoc-html-run6" data-mdoc-js></div>
+Many standard CSS terms can be imported using:
import tyrian.CSS.*
- <div id="mdoc-html-run7" data-mdoc-js></div>
+If you find we've missed a tag or attribute or something, please raise an issue. In the meantime, you can always make your own. Here are just a few made up examples, each of these has numerous constructors for you to explore:
@@ -208,15 +208,15 @@<div id="mdoc-html-run8" data-mdoc-js></div>
+Note that everything is stringly typed, and that's because in HTML everything is stringly typed too! In the generated tags, we've added support for things like attributes acceptingInt
s andBoolean
s and so on where they have known acceptable input types.
One occasion where you may need to make your own tags or attributes etc., is when Tyrian has an incorrectly declared definition of something or other.
A previous case of this was where the input field value
property
was incorrectly declared as an attribute
, and so the value wasn't changed as expected based on model updates.
Effort has gone in to getting these things right, but if you come across any issues, please report them, and the workaround is to re-declare it yourself as above while a fix is produced. - <script type="text/javascript" src="html.md.js" defer></script> - <script type="text/javascript" src="mdoc.js" defer></script>
+ +<div id="mdoc-html-run4" data-mdoc-js></div>
+You can find the full code on the examples directory linked at the top.
<div id="mdoc-html-run5" data-mdoc-js></div>
+Another built-in command is tyrian.websocket.WebSocket
, which has a more complex API.
<div id="mdoc-html-run9" data-mdoc-js></div>
+Moreover, the subscriptions
method handles Web Socket events.
def subscriptions(model: Model): Sub[IO, Msg] =
model.echoSocket.subscribe {
@@ -348,11 +348,11 @@ case
WebSocketEvent.Heartbeat =>
Msg.ToSocket("<💓 heartbeat 💓>")
}
- <div id="mdoc-html-run10" data-mdoc-js></div>
+The full code can be found on the examples linked at the top.
Furthermore, you may also find the trading project useful: a full-stack application with a Web Socket client sharing the back-end domain model. - <script type="text/javascript" src="networking.md.js" defer></script> - <script type="text/javascript" src="mdoc.js" defer></script>
+ +<div id="mdoc-html-run0" data-mdoc-js></div>
+As you can see, this is completely ordinary Scala, which means you can do anything that Scala lets you do in order to generate this HTML block, without having to learn a templating language like Mustache.
The .render
extension method is not strictly necessary since this is now the behaviour of calling .toString
on a tag.
There is an example of SRR in the server-examples.
@@ -177,8 +177,8 @@<div id="mdoc-html-run1" data-mdoc-js></div> - <script type="text/javascript" src="subs.md.js" defer></script> - <script type="text/javascript" src="mdoc.js" defer></script>
+ ++