Skip to content

Latest commit

 

History

History
205 lines (135 loc) · 7.92 KB

F_模板.md

File metadata and controls

205 lines (135 loc) · 7.92 KB

模板

模板 (Templats) 名如其意:即我们传入数据给文件然后完成 HTTP 响应。对于一个 web 应用来说这些响应通常是完整的 HTML 文档。对 API 来说,它们通常是 JSON 或者 XML 格式的数据。模板的内容通常是 html 标签,但也有一些 Elixir 代码混在其中(这些代码 Phoenix 会为我们编译和执行)。事实上 Phoenix 的模板都是预编译的,所以渲染它们非常快。

EEx 是 Phoenix 默认的模板系统,它很像 Ruby 里的 ERB。EEx 是 Elixir 语言的一部分,在生成新应用的时候,Phoenix 使用 EEx 模板系统来创建诸如路由以及主要的视图文件。

我们在 View Guide 中提到过,默认情况下,模板(templates)在 lib/hello_web/templates 目录下,每一个 视图 (view) 对应一个目录,里面包含它们自己的渲染模板的视图模块 (view module)。(原文:Each directory has its own view module to render the templates in it.)

例子

我们已经在前面或多或少的使用了一些模板的功能,尤其是在 Adding Pages GuideViews Guide。 我们会在这里详细介绍。

hello_web.ex

Phoenix 默认会生成一个 lib/hello_web.ex 文件来管理常用的 importsaliases。在 view 代码快中的配置对所有的模板(templates)起作用。

我们来给现有的应用加一点代码来做个实验。

首先我们在 lib/hello_web/router.ex 文件中添加一条新的路由。

defmodule HelloWeb.Router do
  ...

  scope "/", HelloWeb do
    pipe_through :browser # Use the default browser stack

    get "/", PageController, :index
    get "/test", PageController, :test
  end

  # Other scopes may use custom stacks.
  # scope "/api", Hello do
  #   pipe_through :api
  # end
end

然后在 lib/hello_web/controllers/page_controller.ex 文件中定义一个 text/2 action。

defmodule HelloPhoenix.PageController do
  ...

  def test(conn, _params) do
    render conn, "test.html"
  end
end

我们将要创建一个显示是哪个 controller 和 action 在处理请求的函数。

为了完成这个功能,我们需要在 lib/hello_web.ex 文件中从 Phoenix.Controller 模块里引入 action_name/1controller_module/1 函数。

  def view do
    quote do
      use Phoenix.View, root: "lib/hello_web/templates",
                        namespace: HelloWeb

      # Import convenience functions from controllers
      import Phoenix.Controller, only: [get_flash: 2, view_module: 1,
                                        action_name: 1, controller_module: 1]

      ...
    end
  end

接下来,我们在 lib/hello_web/views/page_view.ex 文件的底部定义一个 handler_info/1 函数 (使用我们刚才引入的 controller_module/1action_name/1 函数。 ), 我们还会定义一个 connection_keys/1 函数稍后使用。

defmodule HelloWeb.PageView do
  use HelloWeb, :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

截至目前,我们有了路由,有了 controller action, 修改了 view , 现在我们需要一个模板显示 handler_info/1 函数的返回值。现在来添加一个 lib/hello_web/templates/page/text.html.eex, 内容如下:

<div class="jumbotron">
  <p><%= handler_info @conn %></p>
</div>

注意 @conn 在模板中是可用的, 通过 assigns 传入。

现在当我们访问 localhost:4000/test ,就能看到 Elixir.HelloWeb.PageController.test 返给我们的页面了。

我们可以在 lib/hello_web/views 中的任何一个 view 文件中定义函数。但需要注意,只有当 view 文件的名字和 templates 的名字相匹配,里面的定义的函数才是可用的,比如说,之前的handler_info 函数,只有在 lib/hello_web/templates/page 的模板(templates)里面才是可用的。

显示列表 ( Displaying Lists )

截至目前,我们仅仅在模板(templates)中显示了简单的字符串,在另外一些 guides 中显示了整数(intergers)。我们怎样将这所有元素显示成一个 list 呢?

答案是使用 Elixir 的列表解析(list comprehensions)。

我们现在改动一下 lib/hello_web/templates/page/test.html.eex , 让它能显示 conn 结构体中的内容。

我们增加一个 header 和一个列表解析,像这样:

<div class="jumbotron">
  <p><%= handler_info @conn %></p>

  <h3>Keys for the conn Struct</h3>

  <%= for key <- connection_keys @conn do %>
    <p><%= key %></p>
  <% end %>
</div>

我们把 connection_keys 函数的返回值作为列表遍历的数据源,注意我们使用了 <%=, 如果你不加 = , 什么都不会显示出来。

现在访问 localhost:4000/test ,就能看到 conn 结构体中所有的 key 了。

在模板之中渲染模板( Render templates within templates )

在上面的例子中,实际显示值的代码是相当简单的:

<p><%= key %></p>

但实际情况往往比较复杂,如果我们在这里放置过多的代码会导致难以阅读。

最简单的解决方法就是使用另一个模板! 模板 (Templates) 仅仅只是函数,所以就像正常代码一样,我们可以将一些小的,目的明确的函数组合成一个大的模板,让设计变得更简洁。我们之前在 布局(Layout)的例子中已经见到过,布局就是由模板一层层拼装起来的。

让我们把上面的片段变成一个小模板,我们创建一个 lib/hello_web/templates/page/key.html.eex 文件,内容如下:

<p><%= @key %></p>

我们需要把 key 变成 @key 因为这是一个新模板, 并不包含列表解析的内容。我们通过 assigns map 的方法将值传进来显示。 @ 实际上是一个宏,将 @key '翻译成' Map.get(assigns, :key)

现在我们有了新模板,只要简单的将原先列表解析中的内容替换成模板即可:

<div class="jumbotron">
  <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>

再次访问 localhost:4000/test . 页面和之前显示的结果一样。

在视图中共享模板 (Shared Templates Across Views)

经常的,我们发现应用中有很多地方都需要渲染相同的一些数据,这里的最佳实践是将这些模板放在公共的目录下,让其能在整个应用中被访问到。

让我们把之前的模板变成一个公共 view 。

key.html.eex 当前是被 HelloWeb.PageView 模块渲染的。这里我们直接使用 render 调用是默认假设当前使用的就是 HelloWeb.PageView, 我们可以详细写明,像这样:

<div class="jumbotron">
  ...

  <%= for key <- connection_keys @conn do %>
    <%= render HelloPhoenix.PageView, "key.html", key: key %>
  <% end %>
</div>

因为我们想让这个模板在 lib/hello_web/templates/shared/ 目录下, 我们先添加一个公共的视图 lib/hello_web/views/shared_view.ex

defmodule HelloWeb.SharedView do
  use HelloWeb, :view
end

然后我们将 key.html.eex 文件从 lib/hello_web/templates/page 目录里移动到 lib/hello_web/templates/shared/ 目录下,然后我们就可以把 lib/hello_web/template/page.text.html.eex 中的 render 调用改为如下 (使用 HelloWeb.SharedView):

<%= for key <- connection_keys @conn do %>
  <%= render HelloWeb.SharedView, "key.html", key: key %>
<% end %>

再次访问 localhost:4000/test , 页面内容和之前一样。