Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rails布局和视图渲染 #37

Open
huruji opened this issue May 8, 2018 · 0 comments
Open

Rails布局和视图渲染 #37

huruji opened this issue May 8, 2018 · 0 comments
Labels

Comments

@huruji
Copy link
Owner

huruji commented May 8, 2018

创建响应

从控制器的角度,创建HTTP响应有三种方法:

  • 调用 render 方法

  • 调用 redirect_to 方法

  • 调用 head 方法,向浏览器发送只含HTTP首部的响应

一个控制器:

class BooksController < ApplicationController
    def index
        @books = Book.all
    end
end

基于“多约定,少配置”原则,在 index 动作末尾并没有指定要渲染的视图,Rails会自动在控制器的视图文件夹中寻找 action_name.html.erb 模板,然后渲染。这里渲染的就是 app/views/books/index.html.erb

使用render方法

render 方法的行为有多种定制方式,可以渲染Rails模板的默认视图、指定的模板、文件、行间代码或者什么也不渲染。渲染的内容可以是 文本JSON 或者 XML,而且可以设置响应的内容类型和HTTP状态码。

渲染同个控制器的其他模板

def update
    @book = Book.find(params[:id])
    if @book.update(book_params)
        redirect_to @book
    else
        render "edit"
    end
end

不想用字符串,也可以使用符号:

render :edit

渲染其他控制器的动作
使用 render 方法,指定模板的完整路径(相对于 app/views)即可。

render "products/show"

为了代码意图更加明显,还可以使用 :template 选项:

render template: "products/show"

渲染任意文件

render file: "/u/apps/warehouse_app/current/app/views/products/show" 

想要渲染 views/books 下的 edit.html.erb 模板,以下方法都行:

render :edit
render action: :edit
render "edit"
render "edit.html.erb"
render action: "edit"
render action: "edit.html.erb"
render "books/edit"
render "books/edit.html.erb"
render template: "books/edit"
render template: "books/edit.html.erb"
render "/path/to/rails/app/views/books/edit"
render "/path/to/rails/app/views/books/edit.html.erb"
render file: "/path/to/rails/app/views/books/edit"
render file: "/path/to/rails/app/views/books/edit.html.erb"

渲染纯文本
使用 :plain 选项,可以把没有标记语言的纯文本发给浏览器,这主要用于响应Ajax或无需使用HTML的网络服务。

render plain: "OK"

渲染HTML
使用 :html 选项可以把HTML字符串发送给浏览器:

render html: "<p>hello, world</p>".html_safe

如果没调用 html_safe 方法,HTML实体会转义

渲染JSON

render json: @product

在需要渲染的对象上无需调用 to_json 方法,使用了 :json 选项,render 方法会自动调用 to_json

渲染XML

render xml: @product

在需要渲染的对象上无需调用 to_xml 方法,使用了 :xml 选项,render 方法会自动调用 to_xml

渲染javascript

render js: "alert('hello, rails')"

此时发送给浏览器的字符串,其MIME类型就是 text/javascript

渲染原始的主体

render body: "raw"

这时候返回的类型是 text/html ,只有在不在意内容类型的时候才应该使用这个选项。大多数时候,使用 :plain:html 选项更加合适。

render 方法的其它选项

render 方法一般还可接受其他5个选项:

  • :content_type

  • :layout

  • :location

  • :status

  • :formats

:content_type选项
默认情况下,Rails渲染得到的结果内容类型为 text/html,如果使用 :json 选项,内容类型为 application/json,如果使用 :xml 选项,则内容类型为 application/xml ,如果需要修改内容类型,可使用 :content_type 选项:

render file: filename, content_type: "application/rss"

:layout 选项
render 方法大部分渲染得到的结果都会作为当前布局的一部分显示,:layout 选项指定使用特定的文件作为布局:

render layout: "special_layout"

当设置为 false 时,则说明不使用布局:

render layout: false

:location选项
用于设置HTTP的location首部:

render xml: photo, location: photo_url(photo)

:status选项
设定HTTP状态码,(在大多数情况下都是200),可以使用HTTP状态码,也可以使用状态码含义设定。

render status: 500
render status: :forbidden

:formats选项
改变格式,值可以是一个符号或者一个数组,默认使用 :html

render formats: :xml
render formats: [:json, :xml]

查找布局

查找布局时,首先在文件夹 app/views/layouts 文件夹中是否有和控制器同名的文件。例如,渲染 PhotosController 中的动作会使用 app/views/layouts/photo.html.erb 或者 app/views/layouts/photos.builder 。如果没有针对控制器的布局,Rails会使用 app/views/layouts/application.html.erbapp/views/layouts/application.builder 。如果没有 .erb 布局,Rails会使用 .builder 布局。

指定控制器的布局
在控制器中使用 layout 声明,可以覆盖默认使用的布局约定:

class ProductsController < ApplicationController
    layout "inventory"
end

若要指定整个应用使用的布局,可以在ApplicationController类中使用layout声明:

class ApplicationController < ActionController::Base
    layout "main"
end

在运行时选择布局
使用符号把布局延后到处理请求时再选择:

class ProductsController < ApplicationController
    layout :products_layout
    
    def show
        @product = Product.find(params[:id])
    end

    private
    def products_layout
        @current_user.special? ? "special" : "products"
end

现在,如果用户是特殊用户,会使用一个特殊的布局渲染。

根据条件设定布局
使用 :only:except 选项,可以设定条件

class ProductsController < ApplicationController
    layout "product", except: [:index, :rss]
end

使用 redirect_to 方法

redirect_to 方法告诉浏览器向另一个URL发起新请求:

redirect_to photos_url

可以使用 redirect_back 把用户带回他们之前所在的页面,页面地址从 http_referer 中获取,不过浏览器不一定会设定,所以需要设定 fallback_location

redirect_back(fallback_location: root_path)

默认 redirect_to 方法把HTTP状态码设为302,如果想要设定其他状态码,可以使用 :status 选项:

redirect_to photos_path, status: 301

使用head方法

head 方法只把首部发送给浏览器,参数是HTTP状态码数字,或者符号形式,选项是一个散列,指定首部的名称和对应的值

head :bad_request
head :created, location: photo_path(@photo)

布局的结构

静态资源标签辅助方法

  • aotu_discovery_link_tag

  • javascript_include_tag

  • stylesheet_link_tag

  • image_tag

  • video_tag

  • audio_tag

aotu_discovery_link_tag 链接到订阅源

<%= auto_discovery_link_tag(:rss, {action: "feed"}, {title: "RSS Feed"}) %>

javascript_include_tag

Rails应用的javascript文件可以存放在三个位置: app/assetslib/assetsvendor/assets。文件的地址可使用相对文档根目录的完整路径或URL。例如,如果想链接到 app/assets、lib/assets 或 vendor/assets 文件夹中名为 javascripts 的子文件夹中的文件,可以这么做:

<%= javascript_include_tag "main" %>

Rails生成的script标签如下:

<script src="/assets/main.js"></script>

同时引入多个文件:

<%= javascript_include_tag "main", "columns" %>

引入外部文件:

<%= javascript_include_tag "http://example.com/main.js" %>

stylesheet_link_tag

类似于 javascript_include_tag

<%= stylesheet_link_tag "main" %>
<%= stylesheet_link_tag "main", "column" %>

默认情况下, stylesheet_link_tag 创建的链接属性为 media="screen" rel="stylesheet",指定相应的选项可以覆盖默认值:

<%= stylesheet_link_tag "main_print", media: "print" %>

image_tag

生成img标签,默认从 public/images 文件夹中加载文件:

<%= image_tag "header.png" %>

文件名必须指定图像的拓展名

同样可以通过散列指定HTML属性,另外如果没有 alt 属性,
Rails会使用图片的首字母大写的文件名(去掉拓展名)。

<%= image_tag "home.gif" %>
<%= image_tag "home.gif", alt: "Home" %>

video_tag

生成 <video> 标签,默认从 public/vedios 文件夹中加载文件。

<%= video_tag "movie.ogg" %>

生成

<video src="/videos/movie.ogg" />

同样也支持散列指定HTML属性。
把数组传递给 video_tag 方法可以指定多个视频

<%= video ["trailer.ogg", "movie.ogg"] %>

生成

<video>
    <source src="trailer.ogg" />
    <source src="movie.ogg" />
</video>

audio_tag

生成 <audio> 标签,默认从 public/audio 文件夹中加载

<%= audio_tag "music.mp3" %>

yield

在布局中,yield 标明一个区域,渲染的视图会插在这里,最简单的情况是只有一个 yield ,此时渲染的整个视图都会插入在这个区域:

<html>
    <head></head>
    <body>
    <%= yield %>
    </body>
</html>

表明多个区域:

<html>
   <head>
    <%= yield %>
    </head>
    <body>
    <%= yield %>
    </body>
</html>

视图的主体会插入未命名的yield区域,若想在具名yield中插入内容,可以使用 content_for 方法。

<% content_for :head do %>
   <title>A simple page</title>
<% end %>

<p>Hello, World!</p>

套入布局后生成:

<html>
  <head>
  <title>A simple page</title>
  </head>
  <body>
  <p>Hello, World!</p>
  </body>
</html>

如果不同区域需要不同的内容(sidebar、footer等),就可以使用 content_for 方法。

使用局部视图

<%= render "menu" %>

这会渲染名为 _menu.html.erb 的文件,局部视图的文件名都是以下划线开头的,以便和普通视图区分开,引用时无需加入下划线。

局部布局
与视图使用布局一样,局部视图也可以使用布局

<%= render partial: "link_area", layout: "graybar" %>

这里会使用 _graybar.html.erb 布局渲染局部视图 _link_area.html.erb ,此时局部布局与局部视图保存在同一个文件夹中。

传递局部变量
局部变量可以传入局部视图,这样可以使得局部视图更加强大、更加灵活。

new.html.erb

<h1>New zone</h1>
<%= render partial: "form", locals: {zone: @zone} %>

edit.html.erb

<h1>Editing zone</h1>
<%= render partial: "form", locals: {zone: @zone} %>

_form.html.erb

<%= form_for(zone) do |f| %>
  <p>
    <b>Zone name</b><br>
    <%= f.text_field :name %>
  </p>
  <p>
    <%= f.submit %>
  </p>
<% end %>

每个局部视图中都有一个和局部视图同名的局部变量,通过object选项可以把这个对象传给这个变量:

<%= render partial: "customer", object: @new_customer %>

如果要在局部视图中渲染模型实例,可以使用简写:

<%= render @customer %>

如果要在局部视图中自定义局部变量的名字,可以使用 :as 选项指定:

<%= render partial: "product", collection: @products, as: :item %>
@huruji huruji added the ruby label May 8, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant