【Elm】SPA構成でアンカーリンク(ページ内遷移)の実装

公開:2020/09/26
更新:2020/09/27
2 min読了の目安(約1300字TECH技術記事

追記

SPA構成でない場合

Navigation.load id で簡単に実装することが出来ました。
https://qiita.com/hibohiboo/items/f40315c34d921fba82f0
ただ表題のelm-spa等を利用したSPAの場合、loadだとページのローディングが走って上手く動作しませんでした。
以下SPA利用前提の話です。

やりたかったこと

SPA構成で、フォームで未回答の必須項目があれば、該当項目まで遷移させる

ハマった点

Elm側から#idをつけてhistoryAPIをたたいても、URLの書き換えとレンダリングは走るが、該当箇所まで遷移しない

<!--testの位置まで遷移しない-->
http://example.com/#test

憶測

historyAPIを呼び出したタイミングで、どうもHTMLのレンダリング前にURL生成が終わってしまっている?
遷移先のHTMLが存在してないから遷移できない??

解決策

  • historyAPIは利用しないのでURLはそのまま
  • HTML上でフォームの各項目ごとにidを振る
  • エラーが出た項目のidを取得し、Browser.Dom.getElementで該当項目の位置を取得
  • ブラウザのViewportをBrowser.Dom.setViewportで書き換える

もっと良い方法があれば教えてください!

実装コード

import Browser.Dom as Dom

type Msg
    = SubmitAnswer
    | NotDoing

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        SubmitAnswer ->
            let
                result = // バリデーションロジック
            in
            case result of
                Ok _ -> ・・・

                Err errIndex ->
                    Task.attempt
                    (\_ -> NotDoing)
                    (Dom.getElement ("noAnswer" ++ errIndex)
                        |> Task.andThen
                            (\el ->
                            Dom.setViewport el.element.x el.element.y
                        )
                    )
            
        NotDoing ->
            ( model, Cmd.none )