Scala pour le web : Scala.js
Lorsque l’on développe des applications web, JavaScript est essentiellement un passage obligé1, et on doit soit utiliser ce langage en soi ou utiliser un autre langage qui va compiler en JavaScript (il en existe une panoplie).
Il y a par contre plusieurs propriétés que certains développeurs n’apprécient pas tellement de JavaScript : le typage dynamique, l’héritage par prototypes (et non pas l’héritage hiérarchique comme dans la majorité des langages les plus populaires) et le fait que le langage agisse parfois de façon inattendue2, entre autres.
Scala.js est une tentative de régler une partie des problèmes de JavaScript tout en permettant d’utiliser Scala du côté client et du côté serveur. Ainsi, pour les développeurs travaillant sur un projet découpé de cette façon, il n’est pas nécessaire de changer de langage pour réaliser l’application au complet.
Mise en place de Scala.js
L’outil de build pour Scala le plus utilisé est SBT. Il est donc recommandé de l’installer et de créer un nouveau projet SBT, soit de façon manuelle ou avec votre éditeur (par exemple, sur IntelliJ, il faut installer le plugin Scala qui permet de créer un projet SBT avec un wizard).
Par la suite, il faut ajouter cette ligne dans le fichier project/plugins.sbt
(en remplaçant 0.6.5
par la dernière version) pour installer le plugin de Scala.js dans votre application :
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.5")
Finalement, il faut activer le plugin en ajoutant la ligne suivante dans le fichier build.sbt
:
enablePlugins(ScalaJSPlugin)
Bonjour, monde !
Une fois que le projet est créé, nous pouvons maintenant créer notre première page web avec Scala.js.
Tout d’abord, nous allons créer notre classe Scala qui sera le point d’entrée de notre application web.
import scala.scalajs.js.JSApp
object MonApp extends JSApp {
def main(): Unit = {
println("Bonjour, monde")
}
}
Vous vous doutez bien que ceci fera afficher dans la console du navigateur le simple message Bonjour, monde
.
Pour que ce code Scala devienne du JavaScript, vous devrez taper sbt
en ligne de
commande (on l’a installé plus tôt, vous vous souvenez?) puis taper la commande
qui générera le JavaScript de la façon la plus rapide possible (et non pas nécessairement le fichier le plus petit) : fastOptJS
.3
Pour que le fichier JavaScript soit regénéré automatiquement à chaque fois que le code Scala est sauvegardé, il suffit d’ajouter un tilde devant la commande lors de l’exécution : ~fastOptJS
.
Maintenant, il manque une partie essentielle d’une application web : le fichier HTML.
Voici un exemple de fichier HTML qui ferait l’affaire :
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Test de Scala.js</title>
</head>
<body>
<div id="test"></div>
<!-- On inclut le code Scala.js compilé -->
<script type="text/javascript" src="./target/scala-2.11/scala-js-tutorial-fastopt.js"></script>
<!-- On appelle la fonction générée en JavaScript -->
<script type="text/javascript">
MonApp().main();
</script>
</body>
</html>
Intéropérabilité avec JavaScript
On sait que Scala est intéropérable de façon transparente avec Java. De la même façon, une fonctionnalité très intéressante de Scala.js est le fait que celui-ci soit intéropérable de façon transparente avec JavaScript.
En effet, il contient un type (js.Dynamic
) qui permet de mettre du JavaScript tel quel dans le code. Par exemple, à l’intérieur d’un fichier Scala, on pourrait mettre ce code-ci:
import scala.scalajs.js
//...
val document = js.Dynamic.global.document
val testDiv = document.getElementById("test")
val nouveauP = document.createElement("p")
nouveauP.innerHTML = "Ce message est ajouté par JavaScript"
testDiv.appendChild(nouveauP)
Ce qui est intéressant de noter, c’est que toutes les fonctions qui sont appelées dans ce code sont du JavaScript pur, c’est à dire typées dynamiquement et sans hint de l’éditeur. Ainsi, si on y glisse une erreur, ce n’est qu’à l’exécution que l’erreur sera détectée.
Manipulation du DOM
Ainsi, il serait probablement plus souhaitable de pouvoir faire le même genre de traitement, mais en ayant une validation à la compilation.
C’est tout à fait possible, en ajoutant une librairie pour Scala.js dans notre fichier build.sbt
:
libraryDependencies += "org.scala-js" %%% "scalajs-dom" % "0.8.0"
À quoi ressemblerait le code ci-haut, mais avec cette librairie?
import org.scalajs.dom.document
//...
val testDiv = document.getElementById("test")
val nouveauP = document.createElement("p")
nouveauP.innerHTML = "Ce message est ajouté par JavaScript"
testDiv.appendChild(nouveauP)
Comme vous le voyez, le code est identique, à l’exception du import
et de la ligne
val document = js.Dynamic.global.document
qui n’est plus nécessaire. Par contre, cette fois-ci, lorsque nous sommes dans l’éditeur et qu’une erreur s’y glisse, le compilateur nous avertit immédiatement.
ScalaTags
Typiquement (en particulier lors de la réalisation d’une Single Page Application), on ne veut pas mettre tout notre HTML initial dans la page de départ. On voudrait pouvoir générer du HTML de façon dynamique à partir de Scala. Il existe une librairie qui permet non seulement de le faire, mais aussi de le faire de façon fortement typée. Elle s’appelle ScalaTags.
Par exemple :
document.body.appendChild(
div(
h1(id:="titre", "Ceci est un titre"),
p("Ceci est un paragraphe de texte")
).render
)
Conclusion
Comme vous voyez, on peut avoir bien du plaisir avec cette librairie. Bien entendu, ceci n’est qu’un rapide tour d’horizon de quelques fonctionnalités de base, n’hésitez pas à explorer la documentation et les exemples afin d’en apprendre plus. Par ailleurs, il existe aussi d’excellentes conférences (comme celle-ci) qui permettent de voir de façon plus interactive de quoi il en retourne. Amusez-vous bien!
Merci à JF Bourget et JC Larivière pour leurs commentaires, qui m’ont permis d’améliorer cet article.
-
Du moins, jusqu’à l’utilisation de WebAssembly par les navigateurs. ↩
-
Ce petit vidéo classique de Gary Bernhardt en donne un aperçu, pour JavaScript, et aussi pour Ruby. ↩
-
La commande en production pour avoir le fichier JavaScript le plus petit (mais qui prendra plus de temps à compiler) est
fullOptJS
↩