背景
Scala 3でWebアプリケーションを作成したい。
その際に、HTML, CSSもScalaを使って書きたい。
解決策
Scala 3 で ScalaHTML, ScalaCSSを使う。
ScalaHTML
https://index.scala-lang.org/yrichika/scalahtml
ScalaCSS
https://japgolly.github.io/scalacss/book/index.html
環境
Windows 11
Java: OpenJDK 17.0.7
Scala : 3.3.0
sbt : 1.9.0
Akka: 2.7.0
Akka-HTTP: 10.5.2
※1. Akka, Akka-HTTPについては、ライセンスがBSLなので、無料で使えるのは条件のある環境になります。Scala 3がサポートされているAkka-HTTPを使おうとすると、Akkaは2.7以上を使わなければならないので、必然的にBSLになります。
※2. これから導入するScalaHTMLの要件は3.0 >= Scala >= 2.13.5
ですが、上のAkka-HTTPが3.0.0で動かないので、Scalaのバージョンを3.3.0にしています。一応、コンパイルは通ります。
導入
libraryDependencies
を更新する
libraryDependencies += "io.github.yrichika" %% "scalahtml" % "0.1.1"
libraryDependencies += "com.github.japgolly.scalacss" %% "core" % "0.8.0-RC1"
適宜、Seq()
などを使って書き換えます。
ソースコードを作成する
適宜、プロジェクトの適切な位置にソースコードを置きましょう。
ScalaHTML
package com.isageek.hakushimeita.xnote.client
import scalahtml.Tags._
object MyHtml {
val html = html5() {
"any string or tag methods here"
}
def render() = html
}
ScalaCSS
package com.isageek.hakushimeita.xnote.client
import scala.language.postfixOps
import scalacss.DevDefaults._
object MyStyles extends StyleSheet.Standalone {
import dsl._
"div.std" - (
margin(12 px, auto),
textAlign.left,
cursor.pointer,
&.hover -
cursor.zoomIn,
media.not.handheld.landscape.maxWidth(640 px) -
width(400 px),
&("span") -
color.red
)
"h1".firstChild -
fontWeight.bold
for (i <- 0 to 3)
s".indent-$i" -
paddingLeft(i * 2.ex)
}
※ オフィシャルなドキュメントには載っていませんが、import scala.language.postfixOps
が必要
Akka-HTTP
HTMLファイルを読み込むのではなくて、
先ほど作成したMyHTML
オブジェクトから、直接取得します。
/*
@main def hello: Unit =
println("Hello world!")
println(msg)
def msg = "I was compiled by Scala 3. :)"
*/
/*
* Copyright (C) 2020-2023 Lightbend Inc. <https://www.lightbend.com>
*/
//package docs.http.scaladsl
package $package$.server
import akka.actor.typed.ActorSystem
import akka.actor.typed.scaladsl.Behaviors
import akka.http.scaladsl.Http
import akka.http.scaladsl.model._
import akka.http.scaladsl.server.Directives._
import scala.io.StdIn
import $package$.client.MyHtml
object HttpServerRoutingMinimal {
def main(args: Array[String]): Unit = {
implicit val system = ActorSystem(Behaviors.empty, "my-system")
// needed for the future flatMap/onComplete in the end
implicit val executionContext = system.executionContext
val route =
path("hello") {
get {
// complete(HttpEntity(ContentTypes.`text/html(UTF-8)`, "<h1>Say hello to akka-http</h1>"))
complete(HttpEntity(ContentTypes.`text/html(UTF-8)`, MyHtml.render()))
}
}
val bindingFuture = Http().newServerAt("localhost", 8080).bind(route)
println(s"Server now online. Please navigate to http://localhost:8080/hello\nPress RETURN to stop...")
StdIn.readLine() // let it run until user presses return
bindingFuture
.flatMap(_.unbind()) // trigger unbinding from the port
.onComplete(_ => system.terminate()) // and shutdown when done
}
}
実行
sbt run
するだけ。
built.sbtの設定が不十分なのか、warningっぽいのが出るが、動くので今回は一応無視します。
出力されました。
予定
Scala.jsを使うので、サイトの構成はSPAにしたい。
フレームワークLaminarを導入したいところ。
なので、ScalaHTMLは導入しましたが、たくさん使うわけではありません。
index.htmlになるものを作って、お役目を果たします。
Akka-HTTPは今回の趣旨にあっていて、便利なのだけれど、ライセンスの関係で、プロダクトには開発ライセンスが発生するため、おそらく変えなければならない。いい代わりが見つかるといいですが…