Handlerの生成
プロジェクトを作成し、その構造がなんとなく理解できたので、今度は「Hello World」的な何かを表示させてみます。そのためには新しいViewとそれを呼び出すHandlerが必要です。
yesodのコマンドでHandlerを自動生成することができます。
$ stack exec -- yesod add-handler
コマンドを実行するとHandlerの名前を求められるので入力します。ここでは「Hello」としてます。
Name of route (without trailing R):Hello
次はURLのパスを求められます。/Helloとしておきます。
Enter route pattern (ex: /entry/#EntryId):/Hello
最後に許可するHTTPメソッドを求められるのでこれを指定します。
Enter space-separated list of methods (ex: GET POST): GET
以上でHandlerファイルが自動生成されます。実際にHandler/Hello.hsが追加されているはずです。また、config/routes, Application.hs, my-project.cabalにそれぞれ作成されたHandlerに関する記述が追記されています。
Handlerの編集
Handler/Hello.hsの中身は次のような内容です。
module Handler.Hello where
import Import
getHelloR :: Handler Html
getHelloR = error "Not yet implemented: getHelloR"
この状態でHandler生成時に指定したURL:/Helloをブラウザで見てみると、「Not yet implemented: getHelloR」と書かれたエラー画面が表示されます。ちなみにルートは大文字と小文字を区別するので/helloでは表示されません。
未実装と言われているのでViewのテンプレートを用意し、それを呼び出すようにします。最終行の内容のみコメントアウトし書き換えます。
Handler/Hello.hs
--getHelloR = error "Not yet implemented: getHelloR"
getHelloR = defaultLayout $(widgetFile "hello")
これでこのHandlerが呼び出されると、templates/hello.hamletを使用して画面が生成されるようになります。では早速hello.hamletを作成しましょう。
View(Shakespeare Template)の作成
YesodではViewに関するテンプレートファイルに、なぜかシェイクスピアに関する拡張子がついています。Shakespeare Template と呼ばれ、 Hamlet(HTML) / Cassius(CSS) / Lucius(CSS) / Julius(Javascript) などが有ります。詳細はYesod Web Framework BookのShakespearean Templatesを見るのが良いでしょう。
いずれのテンプレート言語もHaskell側で定義された変数を埋め込むことができます。ほかにもいろいろといいことがあるらしいです。まずはHamlet(HTML)を作ります。Hamletの構文には次のような特徴があります。
- インデントがタグの階層を表すため、閉じタグを必要としない。
- 型安全なHaskellの値を埋め込める
- 手続き的な制御構文を使える
3はまた次の機会に詳しく調べることとして、イメージは次のような感じです。
templates/hello.hamlet
<h1>ハローワールド
<ol start="1">
<li>インデントがタグの階層を表すため、閉じタグを必要としない。
<li>型安全なHaskellの値を埋め込める
<p>例:<a href=@{HomeR}>トップページへのリンク</a>
なれないうちは閉じタグがないのが、若干気持ち悪いです。これで/Helloにアクセスすると、Handlerからhello.hamletが呼び出され表示されます。
次にCassius(CSS) / Lucius(CSS) を見ていきます。CSSには2種類のテンプレートがあります。Cassiusはインデントが階層を表し、Luciusは{}で階層を表します。また、LuciusはCSSのスーパーセットなので、通常のCSSの記法をそのまま使用できます。であればLuciusを使うほうがいいと思います。
templates/hello.cassius
h1
color: #ff0000;
ol
li
color: #00ff00;
templates/hello.lucius
h1{
color: #ff0000;
}
ol{
li {
color: #0000ff;
}
}
特に設定しなくても、同名のテンプレートが適用されます。hello.hasmletにはhello.cassius(lucius)が適用されます。hello.hamletの文字色が変わっていればOKです。
最後にJulius(Javascript) です。JuliusはHamlet同様の値の埋め込みを可能にした拡張Javascriptです。例としてJuliusからトップページへのリンクを動的に生成してみます。文法はJavascriptそのまま、URLをHamlet同様に埋め込んでいます。
templates/hello.julius
document.write("<a href=@{HomeR}>julius link</a>");
以上がView周りの基本です。個人的な感想としては、Hamletにさえなれてしまえば、CSS(Lucius)とJavascript(Julius)は同じ構文プラス埋め込みなので使いこなせればかなり捗りそうです。
ただただし、cassius(lucius), juliusを使わなくとも、通常のcss/jsファイルを読み込んで適用することももちろん可能なので、なれと使い分けが重要です。
Widgetについて
Widgetという変数のようなものを使い、Viewに部分的なViewを埋め込むことができます。実際に上で呼び出しているhamletファイルについても、そのファイルで定義したViewがそのまま表示されているわけではなく、生成されたソースをブラウザで確認すると、BodytタグやHead等々ファイルに記述していないタグがあります。
これらのタグがどこから来ているかというと、templates/default-layout-wrapper.hamletからです。このファイルに記述されたタグにWidgetとしてhamletファイルの内容が埋め込まれています。default-layout.hamletの ^{widget} に埋め込まれた形で、^{pageBody pc} に埋め込まれることになります。
変数の埋め込みやHamletの制御構文についてはまた次回詳しく調べていきます。
参考URL
上記状況でビルドするとエラーになりました。
“`
/home/apo/Documents/project/make/haskell/yesod_20210606/my-project/src/Handler/Hello.hs:7:29: error:
• Couldn’t match expected type ‘WidgetFor App ()’
with actual type ‘Language.Haskell.TH.Syntax.Q
Language.Haskell.TH.Syntax.Exp’
• In the second argument of ‘($)’, namely ‘(widgetFile “hello”)’
In the expression: defaultLayout $ (widgetFile “hello”)
In an equation for ‘getHelloR:
getHelloR = defaultLayout $ (widgetFile “hello”)
|
7 | getHelloR = defaultLayout $(widgetFile “hello”)
| ^^^^^^^^^^^^^^^^^^
“`
Handler/Hello.hsの先頭に
“`
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
“`
を追加したらエラーが消えました。お騒がせしました。