Integrate Http requests with the rest of the updates

此生再无相见时 提交于 2019-12-31 03:04:42

问题


I am making a simple Elm application with the following model:

type alias Model =
    { num : Float
    , str : String
    , list : List Float
    , serverResponse : String
    }

I am following the Todo-MVC example and I have a similar architecture:

type Action
    = NoOp
    | ChangeNum String
    | ChangeStr String
    ...

view : Model -> Html
view model =
    ...

update : Action -> Model -> Model
update action model =
    case action of
    ...

main : Signal Html
main = Signal.map view model

model : Signal Model
model = Signal.foldp update initialModel (Signal.subscribe updates)

initialModel : Model
initialModel =
    ...

updates : Signal.Channel Action
updates = Signal.channel NoOp

I am trying to add a button that will POST the model to some page and in return update model.serverResponse with the server's response. But I am completely stumped.

Can someone help me fill the gap in this code: http://pastebin.com/1irNqh3S


回答1:


Intro

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.

Workaround (hack): JavaScript echo service

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.

Elm code

-- 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

JS code

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);
})

Dangers

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.

Resources

  1. Documentation of Ports
  2. Example project for using ports
  3. A short mailing list discussion about the same problem and solution.
  4. An example Elm game from the ludum dare mini, which uses the same technique for playing and stopping audio. I explained this solution to one of the creators on the #elm IRC channel. Note they had to use a dropRepeats on the outgoing port to keep the echoed events from JavaScript from piling up.
  5. Tentative new APIs for these kind of things in Elm 0.15.


来源:https://stackoverflow.com/questions/28267085/integrate-http-requests-with-the-rest-of-the-updates

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!