2

我正在使用以下模型制作一个简单的 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

4

1 回答 1

3

介绍

目前这比它应该做的要难一些。下一个 Elm 版本(0.15)应该通过新的语言特性和对HttpWebsocket库的改进来解决尴尬问题。

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

解决方法(hack):JavaScript 回显服务

但由于我们仍处于 Elm 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代码

Elm.fullscreen您应该有一个 HTML 文件,您可以在其中使用或启动 Elm 程序Elm.embed。我假设您使用全屏版本:

// 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 代码风格。我希望这足以阻止人们滥用它。我再说一遍,这将在即将到来的 Elm 0.15 中以更好的方式修复。

这种方法的危险在于,您向 Elm 程序发送的事件比在 JavaScript 中获得的事件多。这可能会发生在如此简单的一段 JS 上,它与它所得到的内容相呼应,这可能并不明显。但问题可能来自您的 Elm 程序。如果您的 Elm 程序为通过另一个端口获得的每个字符串从端口发送一个 Http 响应字符串,并且(例如)在其他一些输入更改您的模型时重复该响应,那么您开始累积得到回显的事件。通常 Elm 可以很聪明地处理事件同步,但是有了端口,所有的赌注都没有了,你可以通过累积事件使系统超负荷,从而使程序滞后和浏览器占用内存。所以请小心,不要把这个技巧宣传为好东西。这只是权宜之计。

资源

  1. 端口文件
  2. 使用端口的示例项目
  3. 关于相同问题和解决方案的简短邮件列表讨论。
  4. 来自 ludumdale mini 的Elm 游戏示例,它使用相同的技术来播放和停止音频我在#elm IRC 频道上向其中一位创作者解释了这个解决方案。请注意,他们必须dropRepeats在传出端口上使用 a 来防止来自 JavaScript 的回显事件堆积。
  5. 在 Elm 0.15 中为此类事物提供了暂定的新 API 。
于 2015-02-02T12:55:46.687 回答