TypeScript, Elm and ArcGIS API for JavaScript

TypeScript, Elm and ArcGIS API for JavaScript

Rene Rubalcava | December 6, 2015

I have quite the affinity for learning programming languages and frameworks. It's a guilty pleasure that sometimes I just let myself get drunk on. I recently got hammered on Elm and TypeScript.

I've been doing quite a bit of TypeScript lately. I started learning it a while ago and I thoroughly enjoy it.

About a year or so a ago, I had seen one of Evan Czaplicki's videos on Elm. I was instantly interested. But at the time, I didn't quite grasp it. The syntax of the language has changed a bit since then, removing some confusing bits and I found when I returned to it, it started to click. Just recently, I bought this video and this video to get up to speed again.

I had to build something, anything.

I just talked about vector tiles in the ArcGIS JS API beta 2 release. So I figured, hey, wouldn't it be cool to have an easy way to browser the available vector tiles. I had just finished reading this cool article on how pokedex.org was built. Although, my test app is not nearly as flashy as this one, I was inspired.

So I set out to blow a hipster gasket.

This is what I wanted to build... elm vector tile browser

Simple TypeScript

I dig TypeScript. I've come to appreciate the type checking of the compiler to keep me honest. But I'm also not a type nazi, so I've taken to writing my TypeScript closer to ES2015, with little typings and using the compiler to make sure I don't do something really dumb.

Here is the main.ts for this application. gist

You'll notice that I'm using the amd-dependency method of adding the AMD bits as described here. This is a pretty simple way of loading AMD dependencies into your TypeScript application if you don't have some tds files available.

I create an interface to type the PortalItem I'm using in the bits that I send to the Elm application.

I initialize the Elm app with the embed method. Elm.embed(Elm.PortalItems, mainNode, { portalItems: { items: [] }});.

This will embed the Elm app to a DOM element. I also need to initialize the model used in the application.

Then I can subscribe a port that I've exposed to relay model changes in the Elm app. This is done via Ports in Elm. This provides the updated model then I just check to see of the model property showmap is true, which means I need to create a map based on the updated model.

The TypeScript portion isn't too tricky. It's pretty straightforward.

Query Portal Items -> Initialize Elm with results -> Listen for Elm updates -> Create map when model updated

The bliss of Elm

I'm still learning Elm. It appeals to me. It speaks to me. So I'm not going to pretend this Elm code is awesome and that I'm doing everything right. It's rough, can probably be updated with some sugar, but it works!


Ok, don't get scared. Let's talk about it a bit.

Elm apps all seem to follow a basic pattern.

Model -> Update -> View

This pattern is handled via Signals.

Elm Model

gist Notice that the PortalItem type alias here is the same as the interface I used in TypeScript. I wanted to make sure I was passing the correct data to the Elm app, that's why I used the interface.

The initialModel is the model for the whole application, which is typed to Model, which has a property items which is a list of PortalItems. This could probably be tweaked a bit, but again, it works.

Elm Update

gist You have a type called Action, which has different types of action that can be done in your application. Each action takes some type of argument, except NoOp, which will just return the model.

We have the update that takes two arguments, Action and Model and it returns a Model. Data in Elm is immutable, so you can't simply update the Model, you create a new Model with the updated values.

That's done with the following syntax. { model | items = List.map updateItem model.items }

What this is saying is create a new Model with a new property of items, model | items. And the items is equal to the updated values, List.map updateItem model.items.

See that? Now that you know how that works, look at it again and it's not so bad. This kind of stuff begins to click after a while.

Elm View

I won't go into too much detail on the View stuff, but here are the basics. gist Elm uses a virtual DOM, which makes it fast like some other frameworks that also use a virtual DOM. I won't get into if it is actually faster, but you get the same benefits.

A View takes an Address of an Action, that basically wires up events (read more here) and a Model and it will return Html. It will then render the View based on this model.

I have some methods in here to render the view based on some conditional checks. Should I display all the tiles? Should I display the details of an item? Should I display the map for the item?

Honestly, I struggled with conditional portion of the View in this application. I'm talking kept me up late, had me tossing and turning in my sleep, dreaming up solutions, getting up early to test type struggling. I got it eventually, but this was one of those things that made me feel like an idiot. So now, I feel less like an idiot.

Elm Signals

Signals in Elm is how you wire things up. gist

Elm has this concept of a Mailbox, that matches an address to an action. So when you provide an Address and Action send, it shows up in the Mailbox. You can read more about Mailbox here.

The Signal library has some helpers to assist you.

For example, model = Signal.foldp update initialModel actions. You are going to fold the update we defined earlier with the initialModel and the actions.

actions is defined as Signal.merge inbox.signal (Signal.map UpdateModel portalItems). Here, you are merging the Mailbox signal with the mapping of UpdateModel action and the portalItems in our app. This is where the reactive part comes in. You can read more about how this is all wired together here.

Ok, take a breath.

This looks cool, but how do I actually use it in my JavaScript (or TypeScript) app?

Elm Ports

You expose portions of your application via Ports. gist

You can expose the portalItems so I can send updates into Elm from my JavaScript app. elmApp.ports.portalItems.send({ items });

Then I create a Port called modelChanges I can subscribe on to listen for updates to the PortalItems in the Elm app. elmApp.ports.modelChanges.subscribe()

My brain, it hurts

It took me a while to kind of grasp what's going on in Elm. I'm not going to pretend to be an expert, but I've dabbled in some Haskell and PureScript and even though I find these purely functional languages challenging, I find them fun. I think it's the fact that I know I'm not 100% with them and that they are challenging that I'm drawn to them. They make wish I were smarter, but they also push to me to try harder.

You can view a live demo of the application in action here. It's best viewed on a mobile device. I'll probably tweak it a bit more as I return to it, add some functionality, clean up some styling, but it's a good start.

The source code is available on github.

It's been a little while since I've had a chance to play with a new language, much less build something with it and I had a lot of fun. I think it also shows how easy it really is to integrate the ArcGIS API for JavaScript with not only other frameworks, but other languages that compile to JavaScript.

Experiment and make cool shit