This is a little how-to work with this project.
Table of Contents
What do you need
- sbt installed: SBT
- git installed: Git
- GitHub account: Join Github
- GitHub Pages project: Github Pages
Let’s start
Create your project
The easiest way is just to clone this project:
git clone https://github.com/pme123/github-pages-demo.git
Then just adjust/delete whatever you like, check:
_posts
_config.yml
Prepare your develop environment
To develop you need 2 things:
- Run the Jekyll server:
cd docs bundle exec jekyll serve --baseurl ''
This will also generate the site automatically whenever you change anything.
- In another terminal start a
sbt-task
:sbt ~copyTask
This recompiles the Scala code and copies to the Jekyll server whenever you change something in the
Scala
code.
Push your changes to GitHub
If you never have created a new GitHub repo, please see Using-Git-with-Terminal
If you have the remote Repo and it is linked as described in the tutorial you can commit
and push
your project to GitHub.
git commit -m "created my tutorial"
git push
After a while you see the result on GitHub: https://[github-account].github.io/[github-repo]/
, for example https://pme123.github.io/github-pages-demo/
Add a Post / Blog
Create a page in the _posts
directory.
- The format must be
yyyy-MM-dd-some-name.md
- For example:
2019-04-28-bio-tiger.md
On the top of this page add a header with this format:
---
title: "Title of your post"
excerpt: "An excerpt that is displayed as intro on the overview and as start of your blog."
categories:
- Categorie of your post
tags:
- tags of your blog
---
Here an example:
---
title: "Tigers"
excerpt: "A small introduction on Tigers."
categories:
- Animals
tags:
- animal
- biologie
- tutorial
---
Now start your Post / Blog
- you can use the standard kramdown Markdown_, see https://kramdown.gettalong.org/quickref.html
- or checkout what the Jenkyll Theme provides: So Simple
You can always check the result - just refresh http://localhost:4000
Adjust the navigation links
You can adjust the navigation on the top. Just adjust _data/navigation.yml
Here we add this page:
---
...
- title: How to
url: /develop/2019/04/28/how-to
---
Add a Graph
You can easily add a Graph in the form of \(y=2+x^2\).
In your Post add:
<div id="plotHowTo"></div>
<script>plotGraph("x^3", "plotHowTo", -10, 10);</script>
plotGraph
is a ScalaJS function (seesrc/main/scala/pages/demo/Graph.scala
)
@JSExportTopLevel("plotGraph")
def plotly(
exprStr: String, // the formula you want to draw a Graph for (in form of `x*2` - it mus contain a `x`)
plotDiv: String, // the name of the `<div>` where you want to create the Graph.
rangeFrom: Int = -20, // the range of the x-axe - minimum
rangeTo: Int = 20 // the range of the x-axe - maximum
)
This creates:
Mathematical Expressions
We support MathJax to draw nice mathematical expressions.
You can do it inline, like \(2 * \pi * r = 2 * \pi * 57.2 mm \approx 359.4 mm\) , or as a block. like: \[2 * \pi * r = 2 * \pi * 57.2 mm \approx 359.4 mm\]
Be sure you use \\
!
Example in Markup:
Inline: \\(2 * \\pi * r = 2 * \\pi * 57.2 mm \\approx 359.4 mm\\)
Block: \\[2 * \\pi * r = 2 * \\pi * 57.2 mm \\approx 359.4 mm\\]
Example in Scala:
s"Inline: \\(r = \\sqrt{A/\\pi}\\)"
s"Block: \\[r = \\sqrt{A/\\pi}\\]"
Code your own Graph
To draw Graphs or Diagrams we use Plotly JS. The ScalaJS-Facade is provided by Scalably Typed.
As an example we want to draw a circle with a certain radius.
Lets create a GraphExample.scala
in src/main/scala/pages/demo
:
object GraphExample {
@JSExportTopLevel("exampleGraph")
def exampleGraph(plotDiv: String, radius: Double): Unit = ???
}
The class has a method. It is exported with the annotation @JSExportTopLevel
. Make sure that the name is unique.
In your Markup you can refer it then like:
<div id="howToExampleGraph"></div>
<script>exampleGraph("howToExampleGraph", 5);</script>
The first line provides the div
where the Graph should be painted.
The second line calls the exported Functionality.
Of course you could export the
object
and use it like<script>GraphExample.exampleGraph("howToExampleGraph", 5);</script>
If you have done that, you should see in the Browser console a NotImplementedError
.
So let’s implement the Graph:
@JSExportTopLevel("exampleGraph")
def main(plotDiv: String, radius: Double): Unit = {
val data: js.Array[Data] = js.Array() // we don't have data
val layout: Partial[Layout] =
dynLit(
xaxis = dynLit(
zeroline = true,
range = js.Array(- radius - 0.5, radius + 0.5)
),
yaxis = dynLit(
zeroline = true,
range = js.Array(- radius - 0.5, radius + 0.5)
),
width = 500,
height = 500,
shapes = js.Array( // the only shape is our circle
dynLit(
`type` = "circle",
x0 = -radius,
y0 = -radius,
x1 = radius,
y1 = radius
)
)
).asInstanceOf[Partial[Layout]] // Partial is not very nice done in the Facade
Graph.plot(plotDiv, data, layout) // use the little helper to draw the Graph
}
And that’s how it should look like:
That’s it - make sure you check out Plotly JS, it is amazing what you can do.
Code a Form
Next we want to add a form to our Graph, so we can set the radius ourselves.
For this and especially for the data binding we use Binding.scala.
First let’s extract the plotGraph in its own function:
private def plotGraph(plotDiv: String, radius: Double) = { ... }
Now we define the a field with the radius binding:
val radiusVar: Var[Double] = Var(3.0)
Here is the code to print the form:
@dom
private def printForm(plotDiv: String): Binding[HTMLElement] = {
val r = radiusVar.bind // bind radius
// this code below here is executed whenever the radius changes
plotGraph(plotDiv, r)
val area = r * r * Math.PI
<div class="ui form">
... // a table with the inputs
<input class="right" type="text" name="radiusIn" id="radiusIn"
placeholder="Radius" value={s"$r"}
onblur={_: Event => // adjust the radius on blur
radiusVar.value = radiusIn.value.toDouble
}/>
...
<input class="right" type="text" name="areaIn" id="areaIn"
placeholder="Area" value={s"$area"}
onblur={ _: Event => // adjust the radius with the area
radiusVar.value = Math.sqrt(areaIn.value.toDouble / Math.PI)
}/>
...
</div>
}
We add an onblur
on the input
fields, that sets the new radius after changing the radius or the area.
In the main function we use the Binding.scala render functionality:
@JSExportTopLevel("exampleGraph")
def exampleGraph(plotDiv: String, formDiv: String): Unit = {
dom.render(document.getElementById(formDiv), printForm(plotDiv))
}
See here the complete example: GraphExample.scala
We have to adjust our Markup a bit and add a div
for the form:
<div id="howToExampleGraph"></div>
<div id="howToExampleForm"></div>
<script>exampleGraph("howToExampleGraph", "howToExampleForm");</script>
The result should look then like this:
When you see the result - because the whole form is repainted each time - it is not so satisfing. Can we do better?
Sure we need to make sure that only the parts get repainted that are dynamic.
First we let the plotGraph
take care of the radius
changes.
For that we bind the radius in the plotGraph
and watch it in the main function:
private def plotGraph(plotDiv: String) = Binding {
val radius = radiusVar.bind
...
}
...
@JSExportTopLevel("exampleGraph2")
def exampleGraph2(plotDiv: String, formDiv: String): Unit = {
...
plotGraph(plotDiv).watch
}
This does not help the form but now we don’t have to bind the radius so early in the printFormn
method.
What we do is to extract the inputs
and let them bind the radius themselves, like:
@dom
private def printForm(plotDiv: String): Binding[HTMLElement] = {
<div class="ui form">
...
<td>{radiusInput.bind}</td>
...
}
@dom
private lazy val radiusInput = {
val r = radiusVar.bind
<input class="right" type="text" id="radiusIn2" placeholder="Radius" value={
s"$r"
} onblur={_: Event => // adjust the radius on blur
radiusVar.value = radiusIn2.value.toDouble
}/>
}
See here the complete example: GraphExample2.scala
Now you should see the difference when updating the radius: