Here's how to get two Elm apps on the same page sending and receiving data via Elm's ports system.

This isn't something you're going to want to do in every situation, but it's a useful way to learn how to use ports.

I'll start with an optional video walkthrough, but feel free to scroll down for the code instead.

Here's the code.

The first Elm app is a copy of the basic counter called Buttons from the Elm Guide.

It looks like this:

This app is almost identical to the version from the Guide. The first change is here:

When you're making an Elm module that uses ports, you need to explicitly tell the compiler by declaring it with port module.

The next change is here:

Where the update function in the basic tutorial counter app simply returns the model — that is to say, the number being incremented or decremented — this app also includes a Cmd, and instead of the Cmd.none seen so often in Elm apps, the Cmd here is countOutput updated.

The last change in this file tells the compiler what countOutput is:

It's a port which takes an Int. (Ports can only take types that JSON supports, i.e., strings, integers, floats, booleans, nulls, arrays, or JS objects made up of those types.)

The last big difference between this app called WidgetA and the Buttons app I based it on: WidgetA has no model in its view. Instead, the WidgetA view just shows the UI which you use to increment or decrement the counter. The goal here is to demo sending data from one Elm app to another, so I took that part of the view out of WidgetA and put it another app called WidgetB.

Here's that app:

Like WidgetA, WidgetB is cobbled together from the Buttons example, and a couple other great tutorial examples on elmprogramming.com.

You have to declare port module for this app, just like you did for the other one.

Declaring the port is almost the same as the first one too:

The difference is that countInput is a port which returns a Sub.

That means that the WidgetB app needs to declare it in the subscriptions function:

Subscriptions are where you put code which handles input from outside the system. This subscription simply fires off a ReceivedDataFromJS message, which the update function responds to:

The code doesn't do anything with the ReceivedDataFromJS type, going straight to the model instead, but obviously a UI with more interactivity would include ReceivedDataFromJS in a big case statement covering the other messages that the UI can send.

Anyway, with both widgets set up, you need HTML for the Elm apps to live in, plus a little glue code in JavaScript so the ports can communicate with each other.

Here's what that looks like:

Most of this is Elm boilerplate. Here's the important part:

The above code tells the first Elm app to send all of its output from countOutput to an anonymous function which directs that output into the input port countInput for the second Elm app. This is how you get widgetA.ports.countOutput flowing into widgetB.ports.countInput.

(Note: the naming here is a little awkward. I'm still figuring out the right way to name a port. In the video, I used count as the name for both ports, but on reflection, I like countOutput and countInput better, because they make it explicit that each app has its own unique set of ports.)

So there you have it: a simple demonstration of how to connect one Elm app to another using ports.