When working with Phoenix channels and Elm it may be useful to keep track of the websockets connection status. In this blog post, we’ll see how this can be accomplished by leveraging interoperability.
This blog post won’t cover how to setup Phoenix and Elm together: if you’re interested into that and/or want a primer on how they can fit together, I heavily recommend Alan Gardner’s series of tutorials on Cultivate’s blog.
Moreover, we assume the following versions:
- Phoenix ~> 1.0
- Elm 0.16
Step 1: add the needed elements to the Elm Architecture
Assuming our Elm application is built with the Elm Architecture, we need to make a few changes:
- Update the Model to hold a
connected
property
model =
{ connected : False
, ...
}
- Add a new
Action
to express a connection change event
type Action =
NoOp
| ConnectionChange Bool
| ...
- Extend the main
update
function to handle the newAction
update : Action -> Model -> ( Model, Effects Action )
update action model =
case action of
...
ConnectionChange connected ->
({ model | connected = connected }, Effects.none)
Step 2: Open and wire a port
As we will receive the connected
status value from the outside world, we need to open a port:
port connectionStatusSignal : Signal Bool
Opening a port implies that now we have a new signal that we need to handle, so we need to extend the StartApp
definition:
import Signal exposing (map)
app : StartApp.App Model
app =
StartApp.start
{ init = noFx model
, view = view
, update = update
, inputs =
[ ConnectionChange `map` connectionStatusSignal
]
}
In the code above, we add a new input by mapping the incoming values from our port to the ConnectionChange
action defined before.
Step 3: Update the interop layer
As we added a new port, we need to update the JavaScript initialization step of our Elm application:
var elmApp = Elm.fullscreen(Elm.Main, {
connectionStatusSignal: false
});
Finally, we need to hook into the Phoenix socket
lifecycle to send its status back through the port:
var socket = new Phoenix.Socket("/socket", {});
socket.onOpen(function() {
// fires when connection is opened
elmApp.ports.connectionStatusSignal.send(true);
});
socket.onClose(function() {
// fires when connection is explicitly closed by either
// the client or the channel
elmApp.ports.connectionStatusSignal.send(false);
});
socket.onError(function() {
// fires when an error causes the channel to crash
elmApp.ports.connectionStatusSignal.send(false);
});
This should be it! Feel free to ping @fully_forged on Twitter if you have any questions.