I am making a simple Elm application with the following model:
type alias Model =
{ num : Float
, str : String
, list : List Float
, serverRespon
This is currently a little harder to do than it should be. The next Elm release (0.15) should take care of the awkwardness with a new language feature and a revamp of the Http
and Websocket
libraries among other things.
The basic problem is a cyclic dependency in your signals. You want to create HTTP requests based on your program state ("the current model") and update the program state based on the HTTP responses. This should totally be possible because there is this asynchronous HTTP handling in between, not some senseless recursive definition that cannot be.
But since we're still at Elm 0.14, I'll show you a workaround. Please note that this is a dangerous hack! I'll base this code on the definitions you gave and only repeat names where I redefine. The comments explain what's happening.
-- These are the Http response strings, but coming from JavaScript through a port
port asyncResponses : Signal String
responseActions : Signal Action
responseActions = responseToAction <~ asyncResponses
-- The order of these two parameters of merge may matter. Check which should take precedence.
input : Signal Action
input = Signal.merge responseActions (Signal.subscribe updates)
-- note the redefinition:
main : Signal Html
main = Signal.foldp update initialModel input
-- These are the same Http response strings, but we're sending them out so JavaScript can listen to them.
port responses : Signal String
port responses = Http.send requests |> Signal.keepIf isSuccess (Success "") |> Signal.map (\Success s -> s)
isSuccess response = case response of
Success _ -> True
_ -> False
You should have an HTML file in which you kick off the Elm program with Elm.fullscreen
or Elm.embed
. I'm going to presume you use the fullscreen version:
// Catching the returned object from Elm.fullscreen:
var program = Elm.fullscreen(Elm.Main, {asyncResponses : ""})
// JS Echo Service:
program.ports.responses.subscribe(function(s) {
program.ports.asyncResponses.send(s);
})
I hope it's obvious that jumping through these hoops is annoying and messy and not normal Elm code style. And I hope that's enough to discourage people from abusing this. And I repeat, this is going to be fixed in a nicer way in the upcoming Elm 0.15.
The dangers of this method are that you send more events to the Elm program than you get in JavaScript. It may be non-obvious that this can happen to a such a simple piece of JS that echoes what it gets. But the problem may come from your Elm program. If your Elm program sends an Http response string out of the port for every string it gets through the other port, and also (for example) repeats that response when some other input changes your model, then you start to accumulate events that get echoed. Normally Elm can be clever about event synchronisation but with ports all bets are off and you can overtax the system with accumulating events that make the program lag and the browser hog memory. So please be careful, and don't advertise this trick as a good thing. It's only a stopgap.
dropRepeats
on the outgoing port to keep the echoed events from JavaScript from piling up.