将Http请求与其余更新集成

时间:2015-02-01 19:50:16

标签: elm

我正在使用以下模型制作一个简单的Elm应用程序:

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

我正在关注Todo-MVC示例,我有类似的架构:

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

我正在尝试添加一个按钮,将模型发布到某个页面,然后使用服务器的响应更新model.serverResponse。但我完全难过了。

有人可以帮助我填补此代码中的空白:http://pastebin.com/1irNqh3S

1 个答案:

答案 0 :(得分:3)

简介

目前这比应该做的要困难一些。下一个Elm版本(0.15)应该通过新的语言功能以及HttpWebsocket库的改进来处理尴尬。

基本问题是信号中的循环依赖性。您希望根据程序状态(“当前模型”)创建HTTP请求,并根据HTTP响应更新程序状态。这应该是完全可能的,因为在它们之间存在这种异步HTTP处理,而不是一些无法进行的无意义的递归定义。

解决方法(黑客):JavaScript echo服务

但是因为我们仍然在0.14榆树,我会告诉你一个解决方法。 请注意,这是危险的黑客攻击!我将此代码基于您提供的定义,并且只重复我重新定义的名称。评论解释了发生了什么。

榆树代码

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

您应该有一个HTML文件,您可以使用Elm.fullscreenElm.embed启动Elm程序。我打算假设你使用全屏版本:

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

危险

我希望很明显,跳过这些篮球是令人烦恼和混乱的,而不是正常的榆树代码风格。我希望这足以阻止人们滥用这一点。我再说一遍,这将在即将到来的Elm 0.15中以更好的方式修复。

这种方法的危险在于您向Elm程序发送的事件多于JavaScript中的事件。这可能是不明显的,这可能发生在一个如此简单的JS上,它回应了它所获得的东西。但问题可能来自你的榆树计划。如果你的Elm程序从端口发出一个Http响应字符串,它通过另一个端口发送的每个字符串,并且(例如)当一些其他输入改变你的模型时重复该响应,那么你开始累积得到回应的事件。通常情况下,Elm可以很聪明地进行事件同步,但是对于端口,所有的注意都是关闭的,并且您可以通过累积使程序滞后和浏览器占用内存的事件来使系统过载。所以请小心,不要把这个伎俩宣传为好事。这只是一个权宜之计。

资源

  1. Ports
  2. 的文档
  3. Example project使用端口
  4. 关于同一问题和解决方案的简短mailing list discussion
  5. 来自ludum dare mini的example Elm game,它使用相同的技巧来播放和停止audio。我向#elm IRC channel上的一个创作者解释了这个解决方案。请注意,他们必须在传出端口上使用dropRepeats来保持JavaScript中的回显事件不会堆积。
  6. Tentative new APIs对于Elm 0.15中的这类事情。