どうも、靖宗です。
今回はTemplatesということでテンプレートに関して深掘りしていきます。
大体Viewsで知りたいことは知った感もあるのですが、念のためにチェックしておきます。
Templates
Viewsでも取り扱いましたがテンプレートはHTMLやJSON、XMLなどで利用できます。
基本的にこれらのテンプレートはコンパイル時に読み込まれ、実行中はメモリ上に展開されているようです。なので早いと。
EExというテンプレートシステム(Elixirのライブラリ?)が採用されているようで、RubyでいうところのERBだそうです。自分はRuby触ったことないのでなにがなんだかさっぱりです。
あんまり気にしなくてもビューは作れそうです。
Examples
hello_phoenix_web.ex
とりあえずサンプルを見ていきましょう。 Adding Pagesは経験してる前提で細かい説明はすっ飛ばします。
まずはルーティングを確認しておきます。lib/hello_phoenix_web/router.ex
です。
defmodule HelloPhoenixWeb.Router do use HelloPhoenixWeb, :router ... scope "/", HelloPhoenixWeb do pipe_through :browser get "/", PageController, :index get "/test", PageController, :test end ...
/test
のルーティングが増えてます。なのでコントローラにアクションを追加します。
... def test(conn, _params) do render(conn, "test.html") end ...
ここで、なにやらaction_name/1
とcontroller_module/1
が要る、と書かれておりlib/hello_phoenix_web.ex
を編集します。
... def view do quote do use Phoenix.View, root: "lib/hello_phoenix_web/templates", namespace: HelloPhoenixWeb # Import convenience functions from controllers import Phoenix.Controller, only: [get_flash: 1, get_flash: 2, view_module: 1, action_name: 1, controller_module: 1] ...
たぶんこの章のサンプルの動作に必要なものをインポートしているのでしょう。
細かい事は後回しにして次に進みます。
次はHelloPhoenixWeb.PageView
を編集します。
先ほどインポートしたaction_name/1
とcontroller_module/1
を使ってhandler_info/1
という関数を作っています。
defmodule HelloPhoenixWeb.PageView do use HelloPhoenixWeb, :view def handler_info(conn) do "Request Handled By: #{controller_module(conn)}.#{action_name(conn)}" end def connection_keys(conn) do conn |> Map.from_struct() |> Map.keys() end end
あと、Plug.Connの中にあるキーでも吐き出すようなconnection_keys/1
という関数も追加しました。
たぶん表示される物を見ればなんなのか予想できそうなので今は放置します。
次にテンプレートを追加します。lib/hello_phoenix_web/templates/page/test.html.eex
を作成します。
<div class="phx-hero"> <p><%= handler_info(@conn) %></p> </div>
レイアウトのおかげで書くのはこれだけです。どうやらlocalhost:4000/test
にアクセスしたときにコントローラのモジュール名やアクション名を表示するようなページを書いていたようです。
早速http://localhost:4000/test
にアクセスします。
バッチリです✌('ω')
当たり前っちゃ当たり前なんですが、ここで利用しているhandler_info(@conn)
はこのHelloPhoenixWeb.PageView
でレンダリングされるlib/hello_phoenix_web/templates/page
でしか使えません。
あくまでテンプレートはビュー上で評価されていますので、ビューの関数のみ利用可能です。
Displaying Lists
テンプレートにはリストを表示するディレクティブのようなものもあるようです。
lib/hello_phoenix_web/templates/page/test.html.eex
を下記のように編集します。
<div class="phx-hero"> <p><%= handler_info(@conn) %></p> <h3>Keys for the conn Struct</h3> <%= for key <- connection_keys(@conn) do %> <p><%= key %></p> <% end %> </div>
先ほどと同様にhttp://localhost:4000/test
にアクセスします。
con
のキーが表示されました。
Render templates within templates
テンプレートの構造が複雑になってきて可読性が下がる場合などがあります。
そもそもレイアウト(app.html.eex
)も可読性を上げたりヘッダやフッタなどの冗長性を下げたりする仕組みですが、これはどのテンプレートでも取り入れられます。
つまりテンプレート内でテンプレートを呼び出せます!
早速やってみましょう。lib/hello_phoenix_web/templates/page/key.html.eex
を作成します。
<p><%= @key %></p>
先ほどはテンプレート内で発生した変数key
を利用していましたが、今回はテンプレートをレンダリングする際に渡される変数key
なので@
が着いてます。
今回はショボいですが、これがAmazonの検索結果の1個1個のアイテムだとしたらテンプレートが分かりやすくなり、恩恵を得られそうです。
次にlib/hello_phoenix_web/templates/page/test.html.eex
を編集します。
<div class="phx-hero"> <p><%= handler_info(@conn) %></p> <h3>Keys for the conn Struct</h3> <%= for key <- connection_keys(@conn) do %> <%= render("key.html", key: key) %> <% end %> </div>
keyの変数名を利用していたところにrender/2
関数を呼んでいます。
同様にhttp://localhost:4000/test
にアクセスすれば、先ほどと変わらないページが表示されると思います。
以外とこの辺はJSONの構造などにも利用できそうです。
Shared Templates Across Views
上記で作成したような細かいテンプレートを別のビューで呼び出したい時があると思います。
そういう際には
<div class="phx-hero"> ... <%= for key <- connection_keys(@conn) do %> <%= render(HelloPhoenixWeb.PageView, "key.html", key: key) %> <% end %> </div>
としてモジュール名をきちんと表記してrender/3
を利用することが想定されます。が、いかんせんメンテナンス性に欠けそうです。
そこでPhoenixがベストプラクティスとして提案しているのがSharedView
というものを作成してそこからロードする、といった手法です。
他のビューと同様にlib/hello_phoenix_web/views/shared_view.ex
を作成し、lib/hello_phoenix_web/templates/shared
ディレクトリも作成します。
shared_view.ex
は
defmodule HelloPhoenixWeb.SharedView do use HelloPhoenixWeb, :view end
だけでいいです。(必要に応じてカスタマイズされるかもしれませんが、大体のケースはこれでいいはず)
たとえば今回であればkey.html.eex
をlib/hello_phoenix_web/templates/shared
に入れて利用する場合であればlib/hello_phoenix_web/templates/page/test.html.eex
の該当箇所は
<%= for key <- connection_keys(@conn) do %> <%= render(HelloWeb.SharedView, "key.html", key: key) %> <% end %>
となります。
規模が大きくなったときには有用な方針だと思います。
あくまで方針なので、どうするかは開発者の自由ではあるようです。