by pme123
6 min read

Categories

Tags

This is a little how-to work with this project.

Table of Contents

  1. What do you need
  2. Let’s start
    1. Create your project
    2. Prepare your develop environment
    3. Push your changes to GitHub
  3. Add a Post / Blog
  4. Adjust the navigation links
  5. Add a Graph
  6. Mathematical Expressions
  7. Code your own Graph
  8. Code a Form

What do you need

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:

  1. Run the Jekyll server:
    cd docs
    bundle exec jekyll serve --baseurl ''
    

    This will also generate the site automatically whenever you change anything.

  2. 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 (see src/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: