Phoenixガイドラインを読んでみた(6) - View -

## はじめに

[前回](http://qiita.com/dynamitecoolguy/items/7a8061eb16cf66226ebc)は、Controllerを見てきました。
今回は[View](http://www.phoenixframework.org/docs/views)です。
[次回](http://qiita.com/dynamitecoolguy/items/3abb3b698020283f5c60)は、ガイドラインのTemplateです。

## 視界

PhoenixのViewの主な仕事は二つです。重要な方の仕事は、Template(およびLayout)をレンダリングすること。これを行うのは、`Phoenix.View`に定義された`render/3`関数です。もう一つの仕事は、データを取得したりテンプレートにデータを加工する関数を提供することです。

Phoenixは、Controller、View、テンプレートとの間には絶対的な命名規則があり、`PageController`に対しては`PageView`が必要であり、Templateは`web/templates/page`ディレクトリにある必要があります。

ただし、もし必要であれば、Templateのルートディレクトリは変更することができます。`web/web.ex`に定義されている`HelloPhoenix.Web`モジュールの`view/0`関数で、`:root`キーの値を変更することでルートディレクトリを変更することができます。

Phoenixアプリケーションを生成したときには、三つのViewモジュールが生成されます。`ErrorView`、`LayoutView`、`PageView`の三つで、すべて、`web/views`ディレクトリにあります。

LayoutViewは、以下のようになっています。非常にシンプルです。

```
defmodule HelloPhoenix.LayoutView do
  use HelloPhoenix.Web, :view
end
```

`HelloPhoenix.Web, :view`は、マクロによって`HelloPhoenix.Web`の`view/0`関数を呼び出します。ここでルートディレクトリを`:root`としてセットしています。

ここで、レイアウトファイル`templates/layout/app.html.eex`の中の`<title>Hello Phoenix</title>`を`<title><%= title %></title>`に変更してみます。
次にLayoutViewに関数`title/0`を追加します。

```
defmodule HelloPhoenix.LayoutView do
  use HelloPhoenix.Web, :view

  def title do
    "Hogehoge"
  end
end
```

ここでサーバを起動してアクセスしてみると、タイトルに"Hogehoge"が表示されます。なお、title関数を実装していない場合には、サーバ起動前にコンパイルエラーが発生します。

### Viewについてもう少し

Phoenix.Viewモジュールは、内部で`use Phoenix.Template`を呼び出すことでTemplateに関するさまざまな関数などを使用できるようにしています。

ここで新たな例として、PageView (`web/views/page_view.ex`)を下のように変更します。

```
defmodule HelloPhoenix.PageView do
  use HelloPhoenix.Web, :view

  def message do
    "Viewからのメッセージ"
  end
end
```

次にテンプレート(`web/templates/page/test.html.eex`)を作成します。

```
@付きのメッセージ: <%= @message %>
@なしのメッセージ: <%= message %>
```

今回はブラウザから見るのではなく、直接iexからrender関数を呼び出すことにします。

```
$ iex -S mix
iex(1) Phoenix.View.render(HelloPhoenix.PageView, "test.html", message: "Renderからのメッセージ")

{:safe,
 [[[["" | "@付きのメッセージ: "] | "Renderからのメッセージ"] |
    "\n@なしのメッセージ: "] | "Viewからのメッセージ"]}
```

結果tupleの最初の`:safe`atomは、結果の文字列がレンダリング可能な状態になっていることを示しています。
@付きのシンボルは、render/3の第三引数として与えられたMapから展開されます。@無しのシンボルはElixirコードとして解釈されます。

### Layoutについて

LayoutはTemplateの一種です。したがって、Template同様にView(`web/views/layout_view.ex)を持ちます。
Templateが描画されたとき、LayoutのViewは、@innerに表示されたTemplateの内容が格納されます。この内容は、常に":safe"とされたものです。
また、Layout用のTemplate(`web/templates/layout/app.html.eex`では、<body>内に以下の記述があります。
ここに、Templateが展開されることになります。

```
<%= render @view_module, @view_template, assigns %>
```

### Error View

Phoenixには、ErrorView(`/web/views/error_view.ex`)と呼ばれるViewがあります。このViewの目的は、一般的な二つのエラー、404と500の表示を行うことです。

```
defmodule HelloPhoenix.ErrorView do
  use HelloPhoenix.Web, :view

  def render("404.html", _assigns) do
    "Page not found"
  end

  def render("500.html", _assigns) do
    "Server internal error"
  end

  # In case no render clause matches or no
  # template is found, let's render it as 500
  def template_not_found(_template, assigns) do
    render "500.html", assigns
  end
end
```

エラーが発生したとき、開発環境ではいろいろエラー情報が表示されますが、製品環境ではエラー情報を表示しないようにしなければなりません。これを制御するのが、`config/dev.exs`にある`debug_errors: false`です。

```
use Mix.Config

config :hello_phoenix, HelloPhoenix.Endpoint,
  http: [port: 4000],
  debug_errors: false,
  code_reloader: true,
  . . .
```

エラー表示にTemplateを使う場合は、`web/templates/error`ディレクトリを使いますが、デフォルトでは作成されません。必要なら手動で作成します。