Rails布局和视图渲染

创建响应

从控制器的角度,创建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 %>
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,723评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,485评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,998评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,323评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,355评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,079评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,389评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,019评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,519评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,971评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,100评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,738评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,293评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,289评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,517评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,547评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,834评论 2 345

推荐阅读更多精彩内容

  • 翻译自“Collection View Programming Guide for iOS” 0 关于iOS集合视...
    lakerszhy阅读 3,830评论 1 22
  • 渲染顺序以及继承 渲染页面的时候使用模版 在rails中可以通过指定layout的方式设置不同的控制器使用的布局文...
    李傲娢阅读 568评论 0 0
  • 1、前言 browser-sync的基本原理:首先他会启动一个本地服务,但是如果你已经有一个本地服务,你可以将Br...
    wengjq阅读 1,478评论 0 7
  • 我只想对自己好一点 后来啊,我决定开始减肥了,因为我想要对自己好一点!因为我值得所有最好的一切!因为我想遇到想要的...
    阿_左阅读 272评论 0 0
  • 每天写写心情,其实挺好,不为别的,只想发泄一下内心的情感,好的坏的我都可以一吐为快,不必在乎别人的眼光,不去计较有...
    钝角阅读 774评论 2 43