Writing Angular Services in Scala

Those following my blog posts know that I like to take Scala everywhere. This time, let us write Angular services in Scala.

If you don’t know Angular, it’s a frontend web framework developed by Google in TypeScript, similar to React or Vue. It is a component based framework, and to each component is associated a TypeScript class aiming to control what the HTML component must do. These classes can use services. A service is simply another (usually global) instance of a TypeScript class, either with plenty of facility methods (for example for making http calls), or with global object used to pass information from one component to another.

Our goal today is to discover how one can create and use Angular services in Scala. Using Scala.js, we can compile our Scala code into plain old JavaScript, and export some of our classes to be used, precisely, by the JS world. Let us get started.

TLDR: If you want to jump right into the action, you can head over the accompanying repo. Commits in the repo follow along this article. The master branch shows the final version.

Setup the project

In order for everything to work properly, we simply need a bunch of plugins for managing the project. There are basically three things that we need to do: telling TypeScript what are the types that we are going to provide it, manage the npm dependencies that we want to use, and tell Scala what are the types that exist in JS/TS in the dependencies that we use. Luckily for us, there are exactly three plugins to do just that, to be added inside `project/plugins.sbt`:

addCompilerPlugin(“org.scalameta” % “semanticdb-scalac” % “4.3.10” cross CrossVersion.full)
scalacOptions += “-Yrangepos”
/** Explicitly adding dependency on Scala.js */
addSbtPlugin(“org.scala-js” % “sbt-scalajs” % “1.1.1”)
/** Plugin for generating TypeScript declaration file. */
resolvers += Resolver.jcenterRepo
addSbtPlugin(“eu.swdev” % “sbt-scala-ts” % “0.9”)
/** Plugin for generating Scala.js facades from TypeScript declaration file. */
resolvers += Resolver.bintrayRepo(“oyvindberg”, “converter”)
addSbtPlugin(“org.scalablytyped.converter” % “sbt-converter” % “1.0.0-beta18”)
/** Plugin for managing npm dependencies. */
addSbtPlugin(“ch.epfl.scala” % “sbt-scalajs-bundler” % “0.18.0”)

And now we can simply enable all of these, together with some barebones configuration, in our `build.sbt`:

name := “AngularServices”
version := “0.1”
scalaVersion := “2.13.2”
/** npm module will have version “0.1.0” */
scalaTsModuleVersion := (_ + “.0”)
/** Enabling ScalaJS */
enablePlugins(ScalaJSPlugin)
/** Enabling ScalaTS */
enablePlugins(ScalaTsPlugin)
/** Enabling Scalably typed, with scala-js-bundler */
enablePlugins(ScalablyTypedConverterPlugin)

We are all set to start creating a JS module for Angular.

A first service

One of the strong suit of Scala is its standard collection library. For example, the native JS Array api has no method for taking distinct elements in the array. Let us fix that. Here is a simple implementation which does that (to be put into src/main/scala/angular/ArrayEnhanced.scala):

Well, this came for free. That’s the power of Scala. Let us now generate the JavaScript and the TypeScript declaration file by running the sbt command `scalaTsFastOpt`. After it finishes, you can go have a look into target/scala-2.13/scalajs-bundler/main/angularservices-fastopt.d.ts and you’ll see … nothing! That’s right, because we didn’t actually tell Scala to export this class, nor its members, to the JS world. This is easily done by adding the two annotations to the class:

Issuing the command again, the content of the declaration file is now

Note: the careful reader maybe asked himself why the type of f in the distinctBy method was the weird looking js.Function1[A, B]instead of A => B. This is because the type A => Bis a Scala function, and pure Scala objects do not enter the JS world. Hence, if we had asked for f: A => B, it would have been impossible for JS to give us the correct argument.

Adding the Angular project

Until now, we didn’t do anything specifically related to Angular. Let us do that now. We are going to add an Angular project within the Scala project directory, into `webapp` directory. To that end, we go to `webapp` directory and issue (outside sbt), the command ng new FromScalaWithLove(I chose not to add angular routing and I chose CSS for styling, as we won’t use that.) We can safely delete the content of the app.component.html file, and replace it with

<h1>From Scala, with love</h1>

Issuing ng servein the Angular project FromScalaWithLove, and heading to localhost:4200 should make this title appear.

Using our Scala service

The first thing is to copy paste the files in target/scala-2.13/scalajs-bundler/main into webapp/FromScalaWithLove/node_modules/scala-module. That will make our compiled JS code, together with the type definitions, available to TypeScript. And in app.component.ts, we can add the lines

Saving the file should make Angular recompile and refresh the page. Ew, doing this gratified us with an unfriendly `ERROR NullInjectorError`. This makes sense because we never tell the Dependency Injection (DI) mechanism of Angular to take care of our class. This is easily fixed by adding the ArrayEnhanced type inside the providers array in the app.module.ts file. Saving again should make the error disappear.

We can now happily use the ArrayEnhanced service, for example by adding these lines inside the constructor of the app-component:

Polishing the dev experience

Having to copy-paste the compiled files inside Angular node modules is a tiny bit annoying. We want to automate this process. This is simply done by adding the following lines to the build.sbt file:

After reloading sbt, we can issue the makeModule command, which will copy-paste everything properly. Note that in an actual setup, we would like something a bit more fine tuned than hard-coded paths. For now, however, it will do.

Embracing RxJS

The Angular ecosystem makes heavy use of the FRP library RxJS. An Angular user will then most likely expect a service (with asynchronous behaviour) to return Observables instead of, for example, promises. In order to do that, we once again need to change the build.sbt file, in order to add the npm dependency that we want. In this case, `rxjs`. For the sake of speed, we will also tell our project to use yarn instead of npm. This is done by adding the following lines:

Compile / npmDependencies ++= Seq(“rxjs” -> “6.4.0”)
useYarn := true

(You might want to clean first in sbt in order to avoid some weird shenanigans.) Now you can reload and kick ScalablyTyped off by issuing compile. This is going to take quite some time (probably 2 to 5 minutes), because all the TypeScript definitions have to be compiled into Scala.js facades. But don’t worry, this is only a one time process.

We can now create a Scala class which will expose an Rx observable for Angular to use. Let us start with something very modest:

Issuing the makeModule command should now create a declaration file containing (among others)

Since this command automatically copy-paste into Angular’s node module directory, the shiny new EmitRxObservable service will be available immediately. Don’t forget to register it in the app module providers, though.

Note: you will likely hit the following error:

ERROR in node_modules/scala-module/angularservices-fastopt.d.ts(9,7): error TS1086: An accessor cannot be declared in an ambient context.

which comes from the fact that the interfaced exposed our Scala def as a get accessor. This is “solved” by adding the compilerOption `”skipLibCheck”: true` in `tsconfig.json`. I am not a TypeScript aficionado, so there is perhaps something better to do…

We can now happily consume our service by adding the emitRx: EmitRxObservable to the constructor of the app-component, and for example the line

emitRx.naturalNumbers.forEach((e) => console.log(e));

in the constructor. Upon reload, the console should show the natural numbers getting printed.

More Scala, please!

Up until now, we didn’t really do any Scala. We merely used some JavaScript in disguised, but Scala has a lot to offer. For example, it is very good at manipulating data.

We can define a model User, to be used by TypeScript in the project, directly from Scala. This will be a case class with its member exported:

We add a facility method maybeDateOfBirth to be used inside Scala, as Option[A] is more Scala friendly than A | undefined. However, even if this method will exist in TS (because we export all members), it won’t be usable since an Option is a pure Scala type, hence opaque. More precisely, it will be usable but TS will not be able to do anything with the returned object, except carrying it along and possibly pass it back to a Scala.js function. But TypeScript will be able to create instances of User (since it knows all the types of the constructor), and we can therefore ask for them in a Scala service. One example is done below:

And from TS, this can be used by doing

This shows a tiny bit of what is possible doing Scala: we can define models, let TypeScript instantiate them, and use them as regular Scala objects. The only thing that we need to be careful about is to ask from, and return to, TypeScript, only stuff that it understands.

Let’s go crazy

Scala also has a gigantic ecosystem with high quality libraries. There is no reason we shouldn’t use them for our Angular projects!

Let us imagine this use case: you are creating a dashboard of some sort, and you need to download a certain amount of data. You don’t want to download everything at once. You are then given a list of indices [0, 1, …, n-1]and you need to make a call for each of these. However, you know that some of them will take longer than others to be processed by your backend, so you don’t want to have these guys be a bottleneck. Also, sometimes your calls fail for some reason (not that often, but it happens) and in these cases you would like to retry twice with some back-off.

Ultimately, this is what you want to do:

  • make n http calls to your backend,
  • always 3 of them concurrently
  • retry twice those who fail,
  • if, despite the two retries, one call still fail, the whole process should fail,
  • be able to track the progress,
  • return an observable which will emit once an array with the result of the n calls, in such a way that the j-th element is the result of mapping j, and
  • give the user the possibility to cancel the process before completion.

Good news, this is going to be piece of cake!

We are going to use the ZIO library for that. Other good choices could be AKKA stream or Monix. The first thing to do is to add the ZIO dependency to our project. Add the following line to the build.sbt file:

libraryDependencies += “dev.zio” %%% “zio” % “1.0.0-RC21–2”

We will also need the implementation of the comprehensive java.time library for Scala.js, available via

libraryDependencies += “io.github.cquiroz” %%% “scala-java-time” % “2.0.0"

Function signature

The function that we are going to expose to TypeScript will have the following signature:

where program argument is thought of as an asynchronous observable emitting only once an element of type U. We could also ask for a function returning a js.Promise. We choose the Observable type because it is the one returned by Angular’s `HttpClient`, for example. Note that we only need to export the members of the CompletionState class, because TypeScript never needs to create an instance. It is only required that it understands the ones we are going to give back.

From Rx Observable to ZIO effect

We need to turn this program function into a ZIO effect that we are going to use afterwards. The program assumes that the returned observable might fail, so we need to take that into account. ZIO has us covered and has the function effectAsync to do just that:

This function thus lifts an observable returning a U into a ZIO effect that might fail with a js.Error, and might succeed with a U. Note that you could very well preserve the fact that in TypeScript, an error can really be “anything”. In that case, we would have asked the ZIO effect to fail with a js.Any instead of js.Error.

The retry policy

We decided to allow each program to fail a certain number of times. In ZIO, you need to provide a “retry policy” describing the rules to follow in the retry. We can build a retry policy that fits our needs by doing

If the reader is not familiar with ZIO and wonders why this thing does what we want, they can head over here.

The global execution plan

The last ZIO piece is a pure function taking the inputs from TS, and a bunch of small helper ZIO effect that are going to be actually built by using Rx Observables. Here is the function

The program argument is the program provided by TS, lifted to ZIO. The nextProgress effect will notify that a new program has finished. The two effects complete and fail happen at the end, the former when the whole thing succeeds and the latter when it fails. As we see, the implementation is pretty straightforward. The funny symbol <* means that the right effect will be executed after the left one, but its result will be discarded (similar to Rx tap operator).

The bridge to JS world

We are now ready to implement our function. We create observables for ingesting the progress and the output, and we lift that to ZIO. We then run the program as a cancellable future, and expose a JavaScript function to cancel it. Here is the full implementation:

And that is all. The nice thing that we get from this is that our execution function is pure and can easily be tested. The Scala compiler is also able to ensure us that the global program will never fail. That means that, from TS’ side, we can be certain that the only errors happening will be the ones coming from the input programs, or the TaskCancelledError in the event that TS cancels it.

Using it

We can now use our powerful function from Angular. The interested reader will find in the repo an integration with Angular UI. Below, we simply mention a usage with the console.

Let us write a “dummy” program simulating an asynchronous computation:

This program returns the input one second later, failing with probability 0.1. It prints the input in case of success, and warn “boom” the input in case of failure. Injecting an instance of ZIOService in our component, we can for example use our function like this:

You will be able to see that

  • elements get printed 3 by 3
  • the progress will be displayed accordingly
  • from time to time, a “boom j” will be displayed, and you will see that the value will be printed after bigger numbers
  • the result array printed at the end is well ordered, as expected.

If you want to see the cancellation in action, you can for example do

I hope this example demonstrated that Scala, with the help of ZIO, can give you an enormous amount of power via a straightforward interface. It’s now time to draw conclusions from all of this.

Why and when do this?

Why should one make Angular Services in Scala? I believe there are a variety of good reasons that I try to discuss below:

  • your backend is in Scala. In that case, you will be able to define all your business models for the backend, and expose them to JS/TS to be used immediately. And you can write, in Scala, the type-safe versions of the http calls that you want to make to your backend endpoints. That way, all of your models will be completely in sync, you will be able to have an efficient Scala-to-Scala communication (the ScalaTs repo actually has an example of that).
  • you need to make very advanced stuff, like above. Using ZIO is only one possible example. But Scala has a lot to offer and is perfectly suited to model complicated business domains.
  • you want to go “all in” and make all your services in Scala, leaving to TypeScript and Angular only the responsibility of the controllers. That way, you can have a nice and clean Scala project, exposing just the right amount of information to your components. You are forced (in a good way) to keep a clear separation of concerns between components and services
  • testing your services will be a lot easier. Scala has amazing test libraries that will allow you to extensively test your services, mocking their concrete implementations if need be.

Caveats

There is no such thing as a silver bullet in computer science. This technology is no exception. I can see at least three “drawbacks” that I personally think should not keep you away from choosing it, but you should be aware that they exist.

  • bundle size: the compiled JavaScript file from Scala.js is one big fat file of easily 4 mb. In today’s fashion of doing single page applications, this should not be too much of a deal. But it certainly means that you shouldn’t do this only for the `distinct` method, as shown above
  • scalably typed typings: scalably typed generates Scala.js facades from TS types for you. Given the nature of TypeScript, they are sometimes a bit cumbersome to work with. If you happen to need a fine tuned facade for one of your libraries, it might be worth writing them by hand. It’s really not that hard
  • The ScalaTs plugin is young: the following months, perhaps you will find some very advanced use case that the plugin is not able to handle. No worries, you can still write things down by hand, and raise an issue!

Conclusion

Writing Angular services in Scala is amazing. To me, the advantages largely outweigh the caveats. Especially if your backend is in Scala. The beautiful thing is that most of the above apply not only to Angular, but to any JavaScript/TypeScript project (even node.js ones!).

We did not cover using Angular-Angular services within our Scala services, but it is certainly possible to do so.

Don’t hesitate to give it a try! It is easy to get working with and, who knows, it can be a nice entry point for you into Scala…

Mathematician, Scala enthusiast, father of two.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store