Elmで複数ページからなる極簡単なSPA(シングルページアプリケーション)を作成(ソースコードあり)
2019年6月8日
前回は、『基礎からわかるElm』 をとにかく写経して、SinglePageApplicationを作成しました。
Navigationのところが、いまいち理解できないので、An Introduction to Elm のナビゲーションのところを写経してみたいと思います。
→ と思ったら、『基礎からわかるElm』を写経してみる(5)ナビゲーション のコードが、 An Introduction to Elm のナビゲーションのコードと一緒だったことに気づきました。なので、今回は少しだけVIEWのところを変えてみたいと思います。
- 1. 開発環境
- 2. 作るもの
- 3. 新規Elmアプリの作成
- 4. Elmでbulmaを使用し、Netlifyにデプロイするための準備
- 5. src/Main.elmの新規作成
- 6. MODELの作成
- 7. pageがloadされるってどういうこと?
- 8. pushUrl 関数は何をしてくれるか?
- 9. Navigate within Page (アプリ内でのナビゲーション)
- 10. Navigate to other Pages(アプリ外へのリンク)
- 11. UPDATEの作成
- 12. VIEWの作成
- 13. elm/urlについて
- 14. bulmaで少しだけ見栄えを変更
- 15. GitHubへpush
- 16. Netlifyにデプロイ
- 17. ソースコードとDEMOサイト
スポンサーリンク
開発環境
Windows 10 Pro
Chrome
VisualStudioCode 1.32.3
git version 2.20.1.windows.1
nvm 1.1.7
node 10.2.0
npm 6.4.1
elm 0.19.0-bugfix6
elm-format 0.8.1
VisualStudioCodeの拡張機能でelmをインストールして、settings.jsonに以下のようにelmを設定。
(『Alt + Shift + F』と『Ctrl + S』を使用。)
"[elm]": {
"editor.formatOnSave": true
},
elm-live 3.4.1
elm-test 0.19.0-rev6
作るもの
- 複数ページからなるSPA(SinglePageApplication)
- リンクをクリックすると、ページが変わる
ソースコード
https://github.com/adash333/elm-navigation2
DEMOサイト
https://determined-colden-a15e32.netlify.com/
新規Elmアプリの作成
C:/elm/ フォルダに、elm-intro-navigation/ フォルダを作成し、VisualStudioCodeで開き、Ctrl+@でコマンドプロンプトを開き、以下を入力します。
途中で何か聞かれたらEnterを押します。
elm init
elm install elm/url
Elmでbulmaを使用し、Netlifyにデプロイするための準備
次に、
https://github.com/adash333/elm-github-viewer1
から、.gitignore, index.html, netlify.toml をコピペします。
index.html
netlify.toml
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
src/Main.elmの新規作成
src/Main.elmを新規作成し、以下を入力します。SPA(SinglePageApplication)を作成するため、main関数はBrowser.applicationを用いることになります。
参考:Elm 0.19 の初期化方法 6 種類
module Main exposing (main)
import Browser
import Browser.Navigation as Nav
import Html exposing (..)
import Html.Attributes exposing (..)
import Url
-- MAIN
main : Program () Model Msg
main =
Browser.application
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
, onUrlChange = UrlChanged
, onUrlRequest = LinkClicked
}
-- SUBSCRIPTIONS
subscriptions : Model -> Sub Msg
subscriptions _ =
Sub.none
-- MODEL
-- UPDATE
-- VIEW
MODELの作成
An Introduction to Elm のナビゲーションのコードのMODELのところを写経してみます。
type alias Model =
{ key : Nav.Key
, url : Url.Url
}
init : () -> Url.Url -> Nav.Key -> ( Model, Cmd Msg )
init flags url key =
( Model key url, Cmd.none )
いきなり、type alias Modelの中身がわけわかりません。
urlはなんとなく、ブラウザのアドレス欄に表示されるURLだとわかるのですが、Nav.keyがこのアプリの何の状態を示しているのかさっぱりわかりません。
このModelを見る限り、アプリのもつ状態としては、
- ブラウザに表示されるURL
- その他の情報(Nav.key) → ???
があるらしいのです。Nav.keyは、Browser.Navigation.keyのことらしく、Browser.Navigationの解説ページを読んでみることにします。
https://package.elm-lang.org/packages/elm/browser/latest/Browser-Navigation
ちょっと軽く読んでみます。
pageがloadされるってどういうこと?
Browser.Navigationでは、pushUrl 関数が、一番重要です。pushUrl 関数は、WEBブラウザのアドレスバーのURLを変化させること無く、ページpageのloadを始めます。
pageがloadされるとき、以下の4つの手順を踏むそうです。
- HTMLドキュメントをrequestすると同時に、ページが真っ白blankになる
- HTMLがloadされると、さらに、<script>または<link>関係がrequestされる
- <script>の中身が、HTMLドキュメントを変化させる
- すべてのアセットassetsがloadされて、ページが表示される
この途中の『ページが真っ白になる』(本では、よく、『ページがちらつく』と書かれています)のが、ユーザー体験を損なうらしいです。(ページか3秒以上真っ白のままだと、ほとんどのユーザーがそのサイトを離れてしまうというデータがあるそうです。個人的には何か表示されていても3秒以上待つ気はしませんが、、、)
pushUrl 関数は何をしてくれるか?
pushUrl 関数は、URLを変更するが、HTMLは変更せずにそのままにします。そのため、ページが真っ白blankになりません。
ページをloadしている間に、loading barや、『サーバからデータをロード中ですよ。』など、表示し、ページを真っ白にする必要が無いです。
Navigate within Page (アプリ内でのナビゲーション)
type Key
URLを変更するためのnavigationコマンドを作成するために、Navigation Key が必要です。
navigationコマンドとして、pushUrl関数、replaceUrl関数、back関数、forward関数があります。
Browser.applicationを用いた場合のみ、Navigation Keyにアクセスすることができます。(Browser.elementやBrowser.documentでは、Navigation KeyやpushUrl関数を使用することはできない。)
このNavigation Keyをよく理解できなかったのですが、とりあえず、
SPAでルーティングを行う(pushUrl関数などを使用する)ためには、アプリの状態(Model)としてNavigation Keyが必要!
と覚えておきます。つまり、MODELのところには、
-- elm initのあと、elm install elm/url をしておく
import Browser
import Browser.Navigation as Nav
import Url
-- MODEL
type alias Model =
{ key : Nav.Key
, url : Url.Url
}
init : () -> Url.Url -> Nav.Key -> ( Model, Cmd Msg )
init flags url key =
( Model key url, Cmd.none )
と書いておけばよいそうです。
pushUrl関数
pushUrl : Key -> String -> Cmd msg
pushUrl関数は、Navigation.KeyとString(URLの文字列)からCmd msgを返す関数です。
ブラウザのURLを変更しますが、ページのloadは行いません。
browser historyに、URLを追加します。
URLの作成については、elm/url パッケージを参照するとよいそうです。
Navigate to other Pages(アプリ外へのリンク)
load関数を用います。
load : String -> Cmd msg
Stringは外部リンクのアドレスとなります。
load関数は、外部リンクのアドレスを受け取り、ページ(Htmlファイル)のloadも行います。
Browser.Navigationのページを読むのはここまでとします。
UPDATEの作成
『基礎からわかるElm』 のp179『SECTION-030 ナビゲーション』のコードを参考に、以下のように写経していきます。
まず、Msgですが、
- リンクをクリックしたとき "LinkClicked"
- ブラウザのURLが変化したとき "UrlChanged" (リンクをクリックしてから、ブラウザのアドレスバーのURLが変化するまで、タイムラグがあります。)
の2つが考えられるとのことで、引数も含めて、以下のようになります。
-- UPDATE
type Msg
= LinkClicked Browser.UrlRequest
| UrlChanged Url.Url
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
-- (2)画面遷移のリクエストを受けたとき
LinkClicked urlRequest ->
--(ここに内部リンクと外部リンクに分けて実装)
-- (3)ブラウザのアドレス欄のURLが変更されたとき
UrlChanged url ->
( { model | url = url }
-- 今回は何もしませんが、本当はここでサーバーからデータをもらうことが多い
, Cmd.none
)
画面遷移のリクエストを受けたときの実装ですが、
- 内部リンク
- 外部リンク
の2つの場合を記載しますが、これは定型文となりますので、覚えるしか無さそうです。
case msg of
-- (2)画面遷移のリクエストを受けたとき
LinkClicked urlRequest ->
case urlRequest of
-- 内部リンクならブラウザのURLを更新する(pushUrl関数)
Browser.Internal url ->
( model, Nav.pushUrl model.key (Url.toString url) )
-- 外部リンクなら通常の画面遷移を行う(load関数)
Browser.External href ->
( model, Nav.load href )
この内部リンクのときにpushUrl関数を使用するため(引数にkey と URLが必要)に、Navigation KeyをMODELに設定する必要があるのですね。よくわかりませんが、そういうものと理解しておきます。
VIEWの作成
VIEWには
- リンクをクリックするための場所
- どのURLのときに、どの内容を表示するか
を記載していくことになります。
-- VIEW
view : Model -> Browser.Document Msg
view model =
{ title = "URL Interceptor"
, body =
[ text "The current URL is: "
, b [] [ text (Url.toString model.url) ]
, ul []
[ viewLink "/home"
, viewLink "/profile"
, viewLink "/reviews/the-century-of-the-self"
, viewLink "/reviews/public-opinion"
]
]
}
viewLink : String -> Html msg
viewLink path =
li [] [ a [ href path ] [ text path ] ]
いらすとや
https://www.irasutoya.com/2016/06/blog-post_575.html から、画像をダウンロードして、index.htmlと同じ階層(一番上の階層)に画像をhome.pngとして保存します。
“/home"のときに、/home.png を表示したい場合は、とりあえず、開発サーバで表示する場合は、以下のように記載する必要がありました。
, case (Url.toString model.url) of
"http://localhost:8000/home" ->
img [ src "/home.png" ] []
"/profile" ->
div [] []
"/reviews/the-century-of-the-self" ->
div [] []
"/reviews/public-opinion" ->
div [] []
_ ->
div [] []
elmに特徴的なところとして、
_ ->
div [] []
を記載しないと、コンパイルエラーが出ていました。
elm-liveで開発サーバで表示してみます。
elm-live src/Main.elm --open -- --output=main.js
elm/urlについて
公式サイトのUrlパッケージのページ
https://package.elm-lang.org/packages/elm/url/1.0.0/ を見てみます。
今回のアプリで抜き出したい部分は、
path
の部分なので、
, case (Url.toString model.url) of
"http://localhost:8000/home" ->
img [ src "/home.png" ] []
のところを、
, case model.url.path of
"/home" ->
img [ src "/home.png" ] []
に変更すればよいのかなと思いました。やってみます。
うまくいきました。
Urlの型が、以下のように、elmの”レコード”型となっているので、URL(http:localhost:8000/home)から、path(/homeの部分)を取り出すためには、
model.url.path
と記載すればよく、返り値はString型となり、文字列と比較することができるのですね。
type alias Url =
{ protocol : Protocol
, host : String
, port_ : Maybe Int
, path : String
, query : Maybe String
, fragment : Maybe String
}
参考:
https://matsubara0507.github.io/posts/2018-12-11-detail-elm-url.html
詳解 elm/url !!
Dec 11, 2018
bulmaで少しだけ見栄えを変更
いらすとや https://www.irasutoya.com/ からもう少し画像をダウンロードしてindex.htmlと同じ階層に保存し、bulmaで少しだけ見栄えを変更してみて、以下を入力して開発サーバで確認してみます。
elm-live src/Main.elm --open -- --output=main.js
GitHubへpush
https://github.com/ から新規リポジトリを作成し、pushします。今回は、elm-navigation2という名前にしました。
ターミナル画面で以下を入力して、production用のmain.jsを作成します。(今回はUglify.jsは無視します。)
elm make src/Main.elm --output=main.js --optimize
git init
git add .
git commit -m "first commit"
git remote add origin https://github.com/adash333/elm-navigation2.git
git push -u origin master
Netlifyにデプロイ
https://www.netlify.com/ にログインして、上記GitHubのリポジトリを選択して、デプロイします。設定欄は空白のままです。
無事、デプロイできたようです。
https://determined-colden-a15e32.netlify.com/
ソースコードとDEMOサイト
ソースコード
https://github.com/adash333/elm-navigation2
DEMOサイト
https://determined-colden-a15e32.netlify.com/
参考:
https://gist.github.com/yuizho/c0b08ee59d3da81891426b17209fff4d
公式ガイドのナビゲーションのサンプルコードに画面遷移を加えた感じのもの
bulma,Elmbulma,Elm,elm/url,navigation,SinglePageApplication,SPA
Posted by twosquirrel
関連記事
『基礎からわかるElm』を写経してみる(8)SPAを設計する(複数ページのElmアプリの作り方part2 : ページごとにelmファイルを作成する)
前回は、 『基礎からわかるElm』 p207 を写経して、複数ページからなるGi ...
Elm0.19でCSSフレームワーク”Bulma”を試してみる(3)surprisetalk/elm-bulma packageのexampleファイルをGitHub Pagesで公開してみる
こちらのVuexの記事を真似して、ElmとBulmaでハンバーガーメニューを実装 ...
Elm0.19でアナログ時計
以下の3つのコードをコピペして、Elm0.19でアナログ時計を作ってみました。 ...
『基礎からわかるElm』を写経してみる(6)ユニットテスト
以下の本を写経しています。以下の環境構築の後、elm-formatとVisual ...
parcelでelmアプリを作成してみる(1)
elmと bulmaでググったら、以下のサイトを見つけたので、写経してみたいと思 ...
ディスカッション
コメント一覧
まだ、コメントがありません