fukuoka.ex代表のpiacereです
ご覧いただいて、ありがとうございます
Phoenixで、render()を使っていると、書く場所によって、書き方が違うのを、少し解説しておこうと思います
なお、「Phoenix」は、ElixirのWebフレームワークです
内容が、面白かったり、役に立ったら、「いいね」よろしくお願いします
コントローラと.eexでrender()の書き方が異なる?
コントローラ(通常ページ、LiveView共に)では、下記のようにrender()を使います
defmodule SampleWeb.PageController do
use SampleWeb, :controller
def index(conn, params ) do
render( conn, page, params: params )
end
end
一方、.eexファイル内(layout.html.eexも含む)で、別の.html.eexファイルを読み込む場合に使うrender()では、以下のように、第一引数がconnではありません
…
<article>
<section>
<%= render( @view_module, "header.html", params: @params ) %>
</section>
</article>
…
コントローラで使うrender()の第一引数を調べてみる
まず、コントローラで使用しているrender()の第一引数を見てみます
以下のようなIO.inspect()によるデバッグコードを追加して、ブラウザ表示を行い、コンソールを確認します
なお、ブラウザ表示時、http://localhost:4000/?param_a=123¶m_b=xyz
とパラメータを付けておきます
defmodule SampleWeb.PageController do
use SampleWeb, :controller
def index(conn, params ) do
IO.puts "=============================="
IO.inspect conn
IO.puts "=============================="
render( conn, page, params: params )
end
end
このデバッグで、第一引数が「%Plug.Conn」であることが確認できます
iex> [info] GET /
iex> [debug] Processing with SampleWeb.PageController.index/2
Parameters: %{"param_a" => "123", "param_b" => "xyz"}
Pipelines: [:browser]
iex> ==============================
iex> %Plug.Conn{
adapter: {Plug.Cowboy.Conn, :...},
assigns: %{},
before_send: [#Function<2.67953897/1 in Phoenix.Controller.fetch_flash/2>,
#Function<0.58261320/1 in Plug.Session.before_send/2>,
#Function<1.1812729/1 in Plug.Logger.call/2>,
#Function<0.102641852/1 in Phoenix.LiveReloader.before_send_inject_reloader/2>],
body_params: %{},
cookies: %{},
halted: false,
host: "localhost",
method: "GET",
owner: #PID<0.32698.1>,
params: %{"param_a" => "123", "param_b" => "xyz"},
path_info: [],
path_params: %{},
port: 4000,
private: %{
SampleWeb.Router => {[], %{}},
:phoenix_action => :index,
:phoenix_controller => SampleWeb.PageController,
:phoenix_endpoint => SampleWeb.Endpoint,
:phoenix_flash => %{},
:phoenix_format => "html",
:phoenix_layout => {SampleWeb.LayoutView, :app},
:phoenix_pipelines => [:browser],
:phoenix_router => SampleWeb.Router,
:phoenix_view => SampleWeb.PageView,
:plug_session => %{},
:plug_session_fetch => :done
},
query_params: %{"param_a" => "123", "param_b" => "xyz"},
query_string: "param_a=123¶m_b=xyz",
remote_ip: {127, 0, 0, 1},
req_cookies: %{},
req_headers: [
{"accept",
"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"},
{"accept-encoding", "gzip, deflate"},
…
],
request_path: "/",
resp_body: nil,
resp_cookies: %{},
resp_headers: [
{"cache-control", "max-age=0, private, must-revalidate"},
{"x-request-id", "FZvM6ESlOHrlYv4AAv2j"},
{"x-frame-options", "SAMEORIGIN"},
…
],
scheme: :http,
script_name: [],
secret_key_base: :...,
state: :unset,
status: nil
}
iex> ==============================
.eexで使うrender()の第一引数は「ページ表示に使っているViewモジュール」
次に、.eexで指定している第一引数をIO.inspect()で見てみます
<%
IO.puts "------------------------------"
IO.inspect @view_module
IO.puts "------------------------------"
%>
…
<article>
<section>
<%= render( @view_module, "header.html", params: @params ) %>
</section>
</article>
…
このデバッグで、第一引数が「SampleWeb.PageView」であることが確認できます
iex> [info] GET /
iex> [debug] Processing with SampleWeb.PageController.index/2
Parameters: %{"param_a" => "123", "param_b" => "xyz"}
Pipelines: [:browser]
iex> ------------------------------
iex> SampleWeb.PageView
iex> ------------------------------
```
# ネタばらし
実は、引数の数が同じ、異なる2つのrender()が、定義されているのです(紛らわしい…)
両方のrender()のリファレンスを確認してみましょう
## コントローラで使用されるrender()は「Phoenix.Controller」の関数
https://hexdocs.pm/phoenix/Phoenix.Controller.html#render/3

## .eexで使用されるrender()は「Phoenix.View」の関数
https://hexdocs.pm/phoenix/Phoenix.View.html#render/3

# 何故、こんな面倒な仕様になっているの?
render()は、本来、ビュー側での定義が、MVC的な責務です(PhoenixのソレがMVCなのかはさておき)
しかし、render()は、コントローラ内から頻繁に呼び出されるため、利便性を上げることを目的に、コントローラ側の関数としても実装されている … というのがPhoenixでの実態と思われます
ちなみに、[コントローラおよびビューのモジュール名のコラム](https://qiita.com/piacere_ex/items/688b3e2542bc67ef52c7#%E3%82%B3%E3%83%B3%E3%83%88%E3%83%AD%E3%83%BC%E3%83%A9%E3%81%A8%E3%83%93%E3%83%A5%E3%83%BC%E3%81%AE%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E5%90%8D%E3%81%8C%E4%B8%8D%E4%B8%80%E8%87%B4%E3%81%97%E3%81%A6%E3%81%84%E3%82%8B%E3%81%A8)でも少し解説していますが、コントローラのモジュール名を基準に、自動的にビューのモジュール名を特定することが可能なため、このような実装が実現しているのでしょう
# p.s.「いいね」よろしくお願いします
ページ左上の  や  のクリックを、どうぞよろしくお願いします:bow:
ここの数字が増えると、書き手としては「ウケている」という感覚が得られ、連載を更に進化させていくモチベーションになりますので、もっとElixirネタを見たいというあなた、私達と一緒に盛り上げてください!:tada: