Upload
boldradius-solutions
View
342
Download
1
Embed Size (px)
Citation preview
SCALA JAVASCRIPTKatrin ShechtmanBoldRadius, Toronto, Canada
Dave SugdenBoldRadius, Gatineau, Quebec
© BoldRadius, 2015 1
How to convince your Manager* to adopt scalajs
*Manager: boss | client | CTO | colleague | that clojurescript guy • Effective front-end development with the scalajs ecosystem
is viable (and preferable!).
• Here are some tools to enable you to persuade whoever you need to.
© BoldRadius, 2015 2
Who are you?1. You know and love scala
2. You have to deal with some non-trivial web front-end
3. You don't scalajs? Lets get you going.
4. You do scalajs? Here are some of our opinions
© BoldRadius, 2015 3
Who are we?• Server Side scala/akka developers
• Relatively new to scalajs
• Have experience writing javascript for the front-end
© BoldRadius, 2015 4
Path to scalajs: shoulders of giantsPart 1
Like many others, our path to scalajs began with @lihaoyi
• scalatagsform( div(cls := "form-group")( label(color := GlobalStyles.textColor)("Cluster Name"), input(tpe := "text", cls := "form-control", value := S.cluster.name, onChange ==> B.updateClusterName) ))
© BoldRadius, 2015 5
Path to scalajs: shoulders of giantsPart 2
• scalarximport rx._val a = Var(1)val b = Var(2)val c = Rx{ a() + b() }println(c()) // 3a() = 4println(c()) // 6
© BoldRadius, 2015 6
© BoldRadius, 2015 7
Javascript, living the dream• JS devs live and die by their setups*
• No different for scalajs... you need a setup
*setup: tooling, framework, module + dependency mgmt etc.
© BoldRadius, 2015 8
Javascript the language(from lihaoyi.github.io/hands-on-scala-js) • Its an OK language with some warts
• Not an easy language to work in at scale (refactoring)
© BoldRadius, 2015 9
Javascript as Platform(from lihaoyi.github.io/hands-on-scala-js)
• No install
• Everywhere
• Sandboxed security
© BoldRadius, 2015 10
A Javascript "setup"• Newcomers are often coming from communities in which
full-stack solutions exist.
• JavaScript tooling often consists of small tools, utilities and libraries that combined builds your code to be used in a browser.
• Variety is huge
© BoldRadius, 2015 11
A Javascript "setup" - Part 11. Babel / CoffeeScript / Typescript / PureScript: transpilers
2. Webpack / Browserify : module bundlers
3. Gulp / Grunt : build systems, task runners, orchestrate processes to work on some files
© BoldRadius, 2015 12
A Javascript "setup" - Part 21. npm: package manager, downloading packages, resolving
dependencies
2. Mocha / Jasmine / Chai / sinon: Test framework
3. AngularJS / Ember / Backbone / React etc.
© BoldRadius, 2015 13
A Scala "setup"You are already a scala expert and familiar with SBT:
You already have 2/3 of your "setup"
© BoldRadius, 2015 14
Scala, SBT, ScalaJsPlugin, Webjars: the first 2/3...
© BoldRadius, 2015 15
The Final Third of your Setup• scalajs-dom, scalatags
• scalarx
• scalajs-react
• autowire
• upickle
• sbt tasks and plugins
© BoldRadius, 2015 16
Scalajs really practical guidehttps://github.com/katrinsharp/scalajs-rpg
• Anatomy of typical Scalajs app
• Each step contains minimal code and dependencies
• Client assets are independent from server
• Proposed architecture
© BoldRadius, 2015 17
1. MAKE SURE YOU SHARE!Server/client code sharing - done right
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.4")
//build.sbtimport org.scalajs.sbtplugin.ScalaJSPlugin.autoImport._name := "scalajs-rpg"lazy val root = project.in(file(".")) .aggregate(jsProject, jvmProject)lazy val cross = crossProject.in(file(".")) .settings( name := "scalajs-rpg", version := "0.1-SNAPSHOT", scalaVersion := "2.11.7") .jvmSettings()// JVM-specific settings here .jsSettings()// JS-specific settings here
© BoldRadius, 2015 18
2. MANIPULATE THIS HTML DOM.And use your first Scalajs facade
"org.scala-js" %%% "scalajs-dom" % "0.8.0"
val mainEl = dom.document.getElementById("main-el")//option AmainEl.innerHTML = s""" |<div>some stuff</div> ... """.stripMargin//option Bval parNode = doc.createElement("p")...mainEl.appendChild(parNode)
© BoldRadius, 2015 19
3. GOT DEPENDENCIES? SBT IT!Use WebJars with sbt-web
addSbtPlugin("com.typesafe.sbt" % "sbt-web" % "1.1.1")
"org.webjars" % "jquery" % "1.11.1" / "jquery.js","org.webjars" % "bootstrap" % "3.3.2" / "bootstrap.js" dependsOn "jquery.js"
<!-- dep in index-fastopt.html --><link rel="stylesheet" type="text/css" href="./js/target/web/web-modules/main/webjars/lib/bootstrap/css/bootstrap.min.css">
© BoldRadius, 2015 20
4. SAFE HTML EDUCATION.With scalatags (scalacss? out-of-scope this time)
"com.lihaoyi" %%% "scalatags" % "0.5.2"
divInMainEl.appendChild( div(`class` := "col-md-8", p(`class` := "red", s"From shared and type safe: $res") ).render)
© BoldRadius, 2015 21
5. DON'T RE-INVENT THE WHEEL!jQuery, Angular, React, (Ionic, Electron) ...
"com.github.japgolly.scalajs-react" %%% "core" % "0.9.0"
val component = ReactComponentB[Unit]("TodoListComponent") .initialState(State(List.empty[Todo], "")) .backend(new Backend(_)) .render((_, S, B) => div( h3("TODO"), TodoList(S.items), form(onSubmit ==> B.handleSubmit, input(onChange ==> B.onChange, value := S.text), button("Add") ) ) ).buildU
© BoldRadius, 2015 22
6. KEEP YOUR DATA IN SYNC!"com.lihaoyi" %%% "scalarx" % "0.2.8"
© BoldRadius, 2015 23
7. TREAT YOUR AJAX CALLS WITH SOME SAFETY!"com.lihaoyi" %%% "autowire" % "0.2.5"
//SHAREDtrait Api { def suggestions(s: String): Seq[Suggestion]}//SERVER: segments: val req = autowire.Core.Request(segments, params)AutowireServer.route[Api](ApiImpl)(req)/* CLIENT:** 1. Call statically typed API** 2. Implement callback that will be called when your Future completes** 3. Use Rx to automatically update the client state*/AjaxClient[Api].suggestions(text).call().foreach(r => currentSuggestions() = r)
© BoldRadius, 2015 24
8. RELEASE YOUR CLIENT TO THE WILD. RAWWRR....
• IF your assets are hosted somewhere else:lazy val ReleaseJsCmd = Command.command("releaseJs") { state => "crossJS/fullOptJS" :: "crossJS/webStage" :: state}...jsSettings( pipelineStages := Seq(cssCompress), commands += ReleaseJsCmd )
• IF you host assets on your app server - package it © BoldRadius, 2015 25
Now you have a setup, whats next?
The real (fun) work begins...
You need to show your prospect that this works.
© BoldRadius, 2015 26
You could...• Find an internal project that requires a UI
• Something your people care about.
• Interactive visual representation of your best stuff.
• Remove all the friction associated with scalajs
• Own the problems
© BoldRadius, 2015 27
Tips• Console / dashboard all the things.
• Use websockets and make that server push
• Use a dark background
© BoldRadius, 2015 28
Our experiment with akka clusterA distributed application with akka cluster presents an opportunity:
• What are the states of my clustered actors?
• What are the dependencies between them?
• What hardware are they on?
© BoldRadius, 2015 29
cluster-console• Subscribe to any cluster's events
• Push events to UI with websockets + akka-http streams
• Safe client-server API with autowire + akka-http
• scalajs-react for ui components
• data binding with scalarx
© BoldRadius, 2015 30
Acknowledgements@lihaoyi
• scalajs-react: David Barri @japgolly
• scalajs-spa-tutorial Otto Chrons @ochrons
© BoldRadius, 2015 31
Highlightsd3 facade
package Layout { trait Layout extends js.Object { def force(): ForceLayout = js.native } trait ForceLayout extends js.Function { def size(mysize: js.Array[Double]): ForceLayout = js.native def charge(number: Double): ForceLayout = js.native def linkDistance(number: Double): ForceLayout = js.native def friction(number: Double): ForceLayout = js.native }}val force = d3.layout.force().size(List[Double](P.width, P.height).toJsArray).charge(-1500).linkDistance(1000).friction(0.9)
© BoldRadius, 2015 32
Highlightsd3 inside scalajs-react
val component = ReactComponentB[Props]("Graph").initialStateP { P => val force = ... val (nodes, links) = // calculate Seq[GraphNode], Seq[GraphLink] State(nodes, links, force) }.backend(new Backend(_)) .render{(P, S, B) => svgtag(SvgAttrs.width := P.width, SvgAttrs.height := P.height)( drawLinks(S.links, P.mode), drawNodes(S.nodes, S.force, P.mode) ) }.componentWillMount { scope => scope.backend.startfixed()}.build
© BoldRadius, 2015 33
HighlightsBack to arguing about more important things:
indexes.flatMap(index => indexes.filter(_ > index).map((index, _)))
vs for { index <- indexes eachOther <- indexes.filter(_ > index) tuple <- Some(index, eachOther) } yield tuple
© BoldRadius, 2015 34
Demohttps://github.com/dsugden/cluster-console
© BoldRadius, 2015 35