您可以在 Elm 中使用 ports 与 javascript 进行通信,以便双向发布和订阅事件。让我们构建一个示例,其中显示带有 masonry 布局的图像列表,单击图像将删除它并触发 masonry 来布局剩余的图像。
由于 Elm 应用程序将向 javascript 发送多种类型的事件,我们可以创建一个单独的端口来发送字符串,然后 javascript 可以对其进行操作。这些字符串将是我们可以在 javascript 中解释的命令,以便告诉 masonry 执行某些操作,例如 "initialize" 和 "imageRemoved"。这个端口还需要一个邮箱,我们可以从 Elm 内部向该邮箱发送消息。
masonryMailbox : Signal.Mailbox String
masonryMailbox =
Signal.mailbox ""
port masonryCommands : Signal String
port masonryCommands =
masonryMailbox.signal
由于您使用的是StartApp,因此您可以在update 函数中返回Effects,因此让我们创建一个函数,该函数将从StartApp 初始化程序和@987654331 内部向此邮箱发送消息@函数。
sendMasonryCommand : String -> Effects.Effects Action
sendMasonryCommand cmd =
let
task =
Signal.send masonryMailbox.address cmd
`Task.andThen` \_ -> Task.succeed NoOp
in
Effects.task task
您可以像这样在StartApp init 函数期间发送"initialize" 命令:
init =
(initialModel, sendMasonryCommand "initialize")
在update函数中,如果我们有RemoveImage String动作,我们可以向javascript发送"imageRemove"命令:
update action model =
case action of
NoOp ->
(model, Effects.none)
RemoveImage url ->
let model' =
{ model
| images = List.filter ((/=) url) model.images
, message = "Removing image " ++ url
}
in (model', sendMasonryCommand "imageRemoved")
现在我们需要连接事物的 javascript 端来监听这些事件。如果 javascript 得到命令"initialize",那么我们可以连接砌体。如果我们得到"imageRemoved" 命令,我们可以告诉masonry 再次触发布局命令。
var app = Elm.fullscreen(Elm.Main);
app.ports.masonryCommands.subscribe(function(cmd) {
var $grid = $('.grid')
if (cmd === "initialize") {
$grid.masonry({
itemSelector: '.grid-item',
percentPosition: true,
columnWidth: '.grid-sizer'
});
$grid.imagesLoaded().progress( function() {
$grid.masonry();
});
} else if (cmd === "imageRemoved") {
$grid.masonry();
}
});
我们还可以连接端口以将事件发送回 Elm。让我们通过在每次砌体完成渲染时向 Elm 发送消息来添加示例。首先,我们将创建一个名为 setMessage 的端口。
port setMessage : Signal String
由于这是 javascript 将发布的端口到,我们只在 Elm 中定义函数签名,而不是函数本身。相反,当我们调用Elm.fullscreen() 时,我们必须从javascript 端给信号一个初始值。 javascript 更改为:
var app = Elm.fullscreen(Elm.Main, { setMessage: "" });
现在,在处理"initialize" 命令的javascript 块中,您可以连接masonry 的layoutComplete 函数以向这个新端口发送消息。
$grid.on("layoutComplete", function() {
app.ports.setMessage.send("Masonry layout complete!");
});
为了在 Elm 中使用来自 setMessage 端口的这些消息,您需要一个 SetMessage String 操作,并且您需要将信号映射到 StartApp inputs 列表中。您的 StartApp 初始化代码将如下所示:
app =
StartApp.start
{ init = init
, view = view
, update = update
, inputs = [ Signal.map SetMessage setMessage ]
}
我在几个要点中提供了一个完整的工作示例。这是gist containing the Elm code,这是gist containing the html and javascript。