能否使用已经构建在Elm应用程序之外的导航来驱动Elm应用程序?

12

我正在处理一个现有的Rails应用程序,其中导航必须继续在后端构建(由于复杂性和时间限制)。预期结果是使用Elm生成某些页面,使用Rails生成某些页面,不使用哈希值,也不进行完整页面重新加载(至少对于Elm页面而言)。导航的简化版本如下:

<nav>
  <a href="rails-page-1">...
  <a href="rails-page-2">...
  <a href="elm-page-1">...
  <a href="elm-page-2">...
</nav>

<div id="elm-container"></div>

我已经试过Elm导航包和elm-route-url,后者可能更接近,除非我基本上误解了该包的能力。

有没有一种方法可以实现这一点? 我已经使用hash标签使其工作,但是没有成功。


我认为将事件监听器附加到锚链接上以防止默认行为,并通过端口将意图传递给Elm是可行的。 - Simon H
@SimonH 看起来是这样,我正在考虑这个选项。如果可能的话,我想尝试在 Elm 中解决这个问题。 - scoots
1个回答

7

使用哈希标签

好吧,你逗笑了我。

我在我的Helpers.elm文件中有这个人,我可以用他来代替Html.Events.click

{-| Useful for overriding the default `<a>` behavior which
   causes a refresh, but can be used anywhere
-}
overrideClick : a -> Attribute a
overrideClick =
    Decode.succeed
        >> onWithOptions "click"
            { stopPropagation = False
            , preventDefault = True
            }

在一个a [ overrideClick (NavigateTo "/route"), href "/route" ] [ text "link" ]上,这将允许中键单击该元素,并使用推送状态更新导航。

你需要的是类似于pushState的JavaScript函数,而且不想破坏中键单击的体验。您可以劫持所有<a>,在其事件上调用preventDefault以阻止浏览器导航,并通过目标的href推入新状态。您可以在事件处理程序中委派导航的<a>。由于Navigation运行时不支持外部监听历史更改(似乎正在使用效果管理器),因此您必须通过端口推送该值 - 幸运的是,如果您正在使用Navigation包,则应已经拥有所需组件。

在 Elm 端,结合 Navigation programs 之一,使用 UrlParser.parsePath。创建一个端口进行订阅,使用与其内部 URL 更改相同的消息。请注意保留 HTML 标签。
import Navigation exposing (Location)


port externalPush : (Location -> msg) -> Sub msg


type Msg
    = UrlChange Location
    | ...


main =
    Navigation.program UrlChange
        { ...
        , subscriptions : \_ -> externalPush UrlChange
        }

页面加载后,使用以下代码:
const hijackNavClick = (event) => {
  // polyfill `matches` if needed
  if (event.target.matches("a[href]")) {
    // prevent the browser navigation
    event.preventDefault()
    // push the new url
    window.history.pushState({}, "", event.target.href)
    // send the new location into the Elm runtime via port
    // assuming `app` is the name of `Elm.Main.embed` or
    // whatever
    app.ports.externalPush.send(window.location)
  }
}


// your nav selector here
const nav = document.querySelector("nav")


nav.addEventListener("click", hijackNavClick, false)

谢谢,我已经采纳了你的建议。使用这个解决方案,是否需要使用标志或端口才能使它完全工作?如果不需要,你能详细说明在 Elm 端发生了什么吗?到目前为止,我的更新没有被触发。 - scoots
嗯,我深入研究了本地代码(https://github.com/elm-lang/navigation/blob/2.0.1/src/Native/Navigation.js),似乎它没有监听更改事件。我已经更新了主要答案,提供了一个端口解决方案。 - toastal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接