Можно ли управлять приложением Elm с существующей навигацией, созданной вне приложения Elm?

Я работаю с существующим 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, возможно, вплотную приблизился к последнему, если я не принципиально не понял возможности пакета.

Есть ли способ сделать это? Я получил работу с использованием хэш-тегов, но без них не было удачи.

Ответ 1

с использованием хеш-тегов

Ну, у тебя есть хихиканье.

У меня есть этот парень в моем файле 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" ], который позволит среднему щелчку элемента, а также использовать состояние push для обновления навигации.

Что вам нужно, это что-то похожее на JavaScript, который работает с pushState, и вы не хотите разрушать медиа- нажмите. Вы можете захватить все <a> s, preventDefault в своем событии, чтобы остановить навигацию от браузера, и нажать новое состояние с помощью целевого href. Вы можете делегировать навигацию <a> в обработчике событий. Поскольку среда выполнения Navigation не поддерживает внешнее прослушивание изменений истории (скорее, похоже, это используется диспетчер эффектов), вам нужно будет вывести значение через порт - к счастью, если вы используете Navigation пакет, у вас уже должны быть части.

В конце Elm используйте UrlParser.parsePath в сочетании с одним из Navigation. Создайте порт для подписки на использование одного и того же сообщения, которое используется для его внутреннего изменения URL.

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)