跳至内容 跳至搜索

Action View URL 助手

提供了一组用于创建链接和获取 URL 的方法,这些方法依赖于路由子系统(参见 ActionDispatch::Routing)。这允许你在视图和控制器中使用相同的格式创建链接。

命名空间
方法
B
C
L
M
P
S
包含的模块

常量

BUTTON_TAG_METHOD_VERBS = %w{patch put delete}
 

此助手可以包含在任何包含路由(routes.url_helpers)URL 助手的类中。这里提供的一些方法只在请求的上下文中有效(例如 link_to_unless_current),该上下文必须通过一个名为 request 的方法来提供。

STRINGIFIED_COMMON_METHODS = { get: "get", delete: "delete", patch: "patch", post: "post", put: "put", }.freeze
 

实例公共方法

button_to(name = nil, options = nil, html_options = nil, &block)

生成一个包含单个按钮的表单,该按钮将提交到由 options 集合创建的 URL。这是确保导致数据更改的链接不会被搜索引擎爬虫或加速器触发的最安全的方法。

你可以通过 html_options 控制表单和按钮的行为。html_options 中的大多数值都会传递给按钮元素。例如,在 html_options 中传递 :class 选项将设置按钮元素的 class 属性。

表单元素的 class 属性可以通过在 html_options 中传递 :form_class 选项来设置。它默认为 "button_to",以便对表单及其子元素进行样式设置。

默认情况下,如果对象未持久化,表单将提交一个 POST 请求;反之,如果对象已持久化,则会提交一个 PATCH 请求。要指定不同的 HTTP 动词,请在 html_options 中使用 :method 选项。

如果 button_to 生成的 HTML 按钮与你的布局不兼容,你可以考虑使用 link_to 方法,并使用 data-turbo-method 属性,具体请参阅 link_to 文档。

Options

options 哈希接受与 url_for 相同的选项。要生成一个没有 [action] 属性的 <form> 元素,请传递 false

<%= button_to "New", false %>
# => "<form method="post" class="button_to">
#      <button type="submit">New</button>
#      <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/>
#    </form>"

html_options 中的大多数值都会传递给按钮元素,但也有一些特殊选项

  • :method - HTTP 动词的符号。支持的动词有 :post:get:delete:patch:put。默认是 :post

  • :disabled - 如果设置为 true,将生成一个禁用的按钮。

  • :data - 此选项可用于添加自定义数据属性。

  • :form - 此哈希将是表单的属性

  • :form_class - 这会控制提交按钮所在表单的 class

  • :params - 将作为隐藏字段在表单中渲染的参数哈希。

Examples

<%= button_to "New", action: "new" %>
# => "<form method="post" action="/controller/new" class="button_to">
#      <button type="submit">New</button>
#      <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6" autocomplete="off"/>
#    </form>"

<%= button_to "New", new_article_path %>
# => "<form method="post" action="/articles/new" class="button_to">
#      <button type="submit">New</button>
#      <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6" autocomplete="off"/>
#    </form>"

<%= button_to "New", new_article_path, params: { time: Time.now  } %>
# => "<form method="post" action="/articles/new" class="button_to">
#      <button type="submit">New</button>
#      <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/>
#      <input type="hidden" name="time" value="2021-04-08 14:06:09 -0500" autocomplete="off">
#    </form>"

<%= button_to [:make_happy, @user] do %>
  Make happy <strong><%= @user.name %></strong>
<% end %>
# => "<form method="post" action="/users/1/make_happy" class="button_to">
#      <button type="submit">
#        Make happy <strong><%= @user.name %></strong>
#      </button>
#      <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"  autocomplete="off"/>
#    </form>"

<%= button_to "New", { action: "new" }, form_class: "new-thing" %>
# => "<form method="post" action="/controller/new" class="new-thing">
#      <button type="submit">New</button>
#      <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"  autocomplete="off"/>
#    </form>"

<%= button_to "Create", { action: "create" }, form: { "data-type" => "json" } %>
# => "<form method="post" action="/images/create" class="button_to" data-type="json">
#      <button type="submit">Create</button>
#      <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"  autocomplete="off"/>
#    </form>"
# File actionview/lib/action_view/helpers/url_helper.rb, line 296
def button_to(name = nil, options = nil, html_options = nil, &block)
  html_options, options = options, name if block_given?
  html_options ||= {}
  html_options = html_options.stringify_keys

  url =
    case options
    when FalseClass then nil
    else url_for(options)
    end

  remote = html_options.delete("remote")
  params = html_options.delete("params")

  authenticity_token = html_options.delete("authenticity_token")

  method     = (html_options.delete("method").presence || method_for_options(options)).to_s
  method_tag = BUTTON_TAG_METHOD_VERBS.include?(method) ? method_tag(method) : "".html_safe

  form_method  = method == "get" ? "get" : "post"
  form_options = html_options.delete("form") || {}
  form_options[:class] ||= html_options.delete("form_class") || "button_to"
  form_options[:method] = form_method
  form_options[:action] = url
  form_options[:'data-remote'] = true if remote

  request_token_tag = if form_method == "post"
    request_method = method.empty? ? "post" : method
    token_tag(authenticity_token, form_options: { action: url, method: request_method })
  else
    ""
  end

  html_options = convert_options_to_data_attributes(options, html_options)
  html_options["type"] = "submit"

  button = if block_given?
    content_tag("button", html_options, &block)
  elsif button_to_generates_button_tag
    content_tag("button", name || url, html_options, &block)
  else
    html_options["value"] = name || url
    tag("input", html_options)
  end

  inner_tags = method_tag.safe_concat(button).safe_concat(request_token_tag)
  if params
    to_form_params(params).each do |param|
      options = { type: "hidden", name: param[:name], value: param[:value] }
      options[:autocomplete] = "off" unless ActionView::Base.remove_hidden_field_autocomplete
      inner_tags.safe_concat tag(:input, **options)
    end
  end
  html = content_tag("form", inner_tags, form_options)
  prevent_content_exfiltration(html)
end

current_page?(options = nil, check_parameters: false, method: :get, **options_as_kwargs)

如果当前请求 URI 是由给定的 options 生成的,则返回 true。

Examples

假设我们在 http://www.example.com/shop/checkout?order=desc&page=1 操作中。

current_page?(action: 'process')
# => false

current_page?(action: 'checkout')
# => true

current_page?(controller: 'library', action: 'checkout')
# => false

current_page?(controller: 'shop', action: 'checkout')
# => true

current_page?(controller: 'shop', action: 'checkout', order: 'asc')
# => false

current_page?(controller: 'shop', action: 'checkout', order: 'desc', page: '1')
# => true

current_page?(controller: 'shop', action: 'checkout', order: 'desc', page: '2')
# => false

current_page?('http://www.example.com/shop/checkout')
# => true

current_page?('http://www.example.com/shop/checkout', check_parameters: true)
# => false

current_page?('/shop/checkout')
# => true

current_page?('http://www.example.com/shop/checkout?order=desc&page=1')
# => true

不同的操作可能共享相同的 URL 路径,但具有不同的 HTTP 方法。假设我们发送了一个 POST 请求到 http://www.example.com/products 并渲染了一个验证错误。

current_page?(controller: 'product', action: 'index')
# => false

current_page?(controller: 'product', action: 'create')
# => false

current_page?(controller: 'product', action: 'create', method: :post)
# => true

current_page?(controller: 'product', action: 'index', method: [:get, :post])
# => true

我们也可以传入符号参数而不是字符串。

# File actionview/lib/action_view/helpers/url_helper.rb, line 559
def current_page?(options = nil, check_parameters: false, method: :get, **options_as_kwargs)
  unless request
    raise "You cannot use helpers that need to determine the current " \
          "page unless your view context provides a Request object " \
          "in a #request method"
  end

  if options.is_a?(Hash)
    check_parameters = options.delete(:check_parameters) { check_parameters }
    method = options.delete(:method) { method }
  else
    options ||= options_as_kwargs
  end

  method_matches = case method
  when :get
    request.get? || request.head?
  when Array
    method.include?(request.method_symbol) || (method.include?(:get) && request.head?)
  else
    method == request.method_symbol
  end
  return false unless method_matches

  url_string = URI::RFC2396_PARSER.unescape(url_for(options)).force_encoding(Encoding::BINARY)

  # We ignore any extra parameters in the request_uri if the
  # submitted URL doesn't have any either. This lets the function
  # work with things like ?order=asc
  # the behavior can be disabled with check_parameters: true
  request_uri = url_string.index("?") || check_parameters ? request.fullpath : request.path
  request_uri = URI::RFC2396_PARSER.unescape(request_uri).force_encoding(Encoding::BINARY)

  if %r{^\w+://}.match?(url_string)
    request_uri = +"#{request.protocol}#{request.host_with_port}#{request_uri}"
  end

  remove_trailing_slash!(url_string)
  remove_trailing_slash!(request_uri)

  url_string == request_uri
end

使用由 options 集合创建的 URL,创建一个给定 name 的锚点元素。有关有效选项,请参阅 url_for 的文档。也可以传递一个字符串而不是选项哈希,这将生成一个使用字符串值作为链接 href 的锚点元素。使用 :back 符号而不是选项哈希将生成一个指向引荐来源(referrer)的链接(如果没有引荐来源,则使用 JavaScript 返回链接)。如果 nil 作为 name 传递,链接本身的值将成为 name。

link_to(body, url, html_options = {})
  # url is a String; you can use URL helpers like
  # posts_path

link_to(body, url_options = {}, html_options = {})
  # url_options, except :method, is passed to url_for

link_to(options = {}, html_options = {}) do
  # name
end

link_to(url, html_options = {}) do
  # name
end

link_to(active_record_model)
  • :data - 此选项可用于添加自定义数据属性。

由于它依赖于 url_forlink_to 支持旧式控制器/操作/id 参数和新式 RESTful 路由。当前的 Rails 风格尽可能倾向于 RESTful 路由,因此请根据资源构建你的应用程序并使用

link_to "Profile", profile_path(@profile)
# => <a href="/profiles/1">Profile</a>

或者更简洁的

link_to "Profile", @profile
# => <a href="/profiles/1">Profile</a>

来代替更冗长、非资源导向的

link_to "Profile", controller: "profiles", action: "show", id: @profile
# => <a href="/profiles/show/1">Profile</a>

同样,

link_to "Profiles", profiles_path
# => <a href="/profiles">Profiles</a>

优于

link_to "Profiles", controller: "profiles"
# => <a href="/profiles">Profiles</a>

当 name 为 nil 时,将显示 href

link_to nil, "http://example.com"
# => <a href="http://www.example.com">http://www.example.com</a>

更简洁地说,当 name 是一个定义了返回默认值或模型实例属性的 to_s 方法的 Active Record 模型时

link_to @profile
# => <a href="http://www.example.com/profiles/1">Eileen</a>

如果你的链接目标难以放入 name 参数,你也可以使用块。ERB 示例

<%= link_to(@profile) do %>
  <strong><%= @profile.name %></strong> -- <span>Check it out!</span>
<% end %>
# => <a href="/profiles/1">
       <strong>David</strong> -- <span>Check it out!</span>
     </a>

CSS 的类和 ID 很容易生成

link_to "Articles", articles_path, id: "news", class: "article"
# => <a href="/articles" class="article" id="news">Articles</a>

使用旧式参数风格时要小心,因为需要额外的字面量哈希

link_to "Articles", { controller: "articles" }, id: "news", class: "article"
# => <a href="/articles" class="article" id="news">Articles</a>

省略哈希将导致错误的链接

link_to "WRONG!", controller: "articles", id: "news", class: "article"
# => <a href="/articles/index/news?class=article">WRONG!</a>

link_to 也可以生成带有锚点或查询字符串的链接

link_to "Comment wall", profile_path(@profile, anchor: "wall")
# => <a href="/profiles/1#wall">Comment wall</a>

link_to "Ruby on Rails search", controller: "searches", query: "ruby on rails"
# => <a href="/searches?query=ruby+on+rails">Ruby on Rails search</a>

link_to "Nonsense search", searches_path(foo: "bar", baz: "quux")
# => <a href="/searches?foo=bar&baz=quux">Nonsense search</a>

你可以设置任何链接属性,如 targetreltype

link_to "External link", "http://www.rubyonrails.org/", target: "_blank", rel: "nofollow"
# => <a href="http://www.rubyonrails.org/" target="_blank" rel="nofollow">External link</a>

Rails 7 默认启用 Turbo。Turbo 提供以下 :data 选项

  • turbo_method: symbol of HTTP verb - 使用给定的 HTTP 动词执行 Turbo 链接访问。表单建议用于执行非 GET 请求。仅在无法使用表单时才使用 data-turbo-method

  • turbo_confirm: "question?" - 向链接添加一个带有给定值的确认对话框。

有关上述选项的更多信息,请参阅 Turbo Handbook。

link_to "Delete profile", @profile, data: { turbo_method: :delete }
# => <a href="/profiles/1" data-turbo-method="delete">Delete profile</a>

link_to "Visit Other Site", "https://rubyonrails.cn/", data: { turbo_confirm: "Are you sure?" }
# => <a href="https://rubyonrails.cn/" data-turbo-confirm="Are you sure?">Visit Other Site</a>

如果 condition 为 true,则使用由 options 集合创建的 URL 创建给定 name 的链接标签,否则只返回 name。要专门化默认行为,你可以传递一个块,该块接受 name 或 link_to_if 的完整参数列表。

<%= link_to_if(@current_user.nil?, "Login", { controller: "sessions", action: "new" }) %>
# If the user isn't logged in...
# => <a href="/sessions/new/">Login</a>

<%=
   link_to_if(@current_user.nil?, "Login", { controller: "sessions", action: "new" }) do
     link_to(@current_user.login, { controller: "accounts", action: "show", id: @current_user })
   end
%>
# If the user isn't logged in...
# => <a href="/sessions/new/">Login</a>
# If they are logged in...
# => <a href="/accounts/show/3">my_username</a>

除非 condition 为 true,否则使用由 options 集合创建的 URL 创建给定 name 的链接标签,在这种情况下只返回 name。要专门化默认行为(例如,显示一个登录链接而不是纯文本链接),你可以传递一个块,该块接受 name 或 link_to_unless 的完整参数列表。

<%= link_to_unless(@current_user.nil?, "Reply", { action: "reply" }) %>
# If the user is logged in...
# => <a href="/controller/reply/">Reply</a>

<%=
   link_to_unless(@current_user.nil?, "Reply", { action: "reply" }) do |name|
     link_to(name, { controller: "accounts", action: "signup" })
   end
%>
# If the user is logged in...
# => <a href="/controller/reply/">Reply</a>
# If not...
# => <a href="/accounts/signup">Reply</a>

除非当前请求 URI 与链接相同,否则使用由 options 集合创建的 URL 创建给定 name 的链接标签,在这种情况下只返回 name(或者如果存在,则执行给定的块)。你可以给 link_to_unless_current 一个块,该块将专门化默认行为(例如,显示一个“开始这里”链接而不是链接文本)。

假设你有一个导航菜单…

<ul id="navbar">
  <li><%= link_to_unless_current("Home", { action: "index" }) %></li>
  <li><%= link_to_unless_current("About Us", { action: "about" }) %></li>
</ul>

如果在“about”操作中,将渲染…

<ul id="navbar">
  <li><a href="/controller/index">Home</a></li>
  <li>About Us</li>
</ul>

…但如果在“index”操作中,将渲染

<ul id="navbar">
  <li>Home</li>
  <li><a href="/controller/about">About Us</a></li>
</ul>

传递给 link_to_unless_current 的隐式块将在当前操作是给定操作时进行评估。因此,如果我们有一个评论页面并想渲染一个“返回”链接而不是评论页面的链接,我们可以这样做…

<%=
    link_to_unless_current("Comment", { controller: "comments", action: "new" }) do
       link_to("Go back", { controller: "posts", action: "index" })
    end
 %>

mail_to(email_address, name = nil, html_options = {}, &block)

创建到指定 email_address 的 mailto 链接标签,该地址也用作链接的名称,除非指定了 name。可以在 html_options 中传递其他 HTML 属性。

mail_to 有几种方法可以通过向 html_options 传递特殊键来定制电子邮件本身。

Options

  • :subject - 预设电子邮件的主题行。

  • :body - 预设电子邮件的正文。

  • :cc - 电子邮件的抄送收件人。

  • :bcc - 电子邮件的密送收件人。

  • :reply_to - 预设电子邮件的 Reply-To 字段。

Obfuscation

在 Rails 4.0 之前,mail_to 提供了编码地址的选项,以阻止电子邮件爬虫。要利用这些选项,请安装 actionview-encoded_mail_to gem。

Examples

mail_to "me@domain.com"
# => <a href="mailto:me@domain.com">me@domain.com</a>

mail_to "me@domain.com", "My email"
# => <a href="mailto:me@domain.com">My email</a>

mail_to "me@domain.com", cc: "ccaddress@domain.com",
         subject: "This is an example email"
# => <a href="mailto:me@domain.com?cc=ccaddress@domain.com&subject=This%20is%20an%20example%20email">me@domain.com</a>

如果你的链接目标难以放入 name 参数,你也可以使用块。ERB 示例

<%= mail_to "me@domain.com" do %>
  <strong>Email me:</strong> <span>me@domain.com</span>
<% end %>
# => <a href="mailto:me@domain.com">
       <strong>Email me:</strong> <span>me@domain.com</span>
     </a>
# File actionview/lib/action_view/helpers/url_helper.rb, line 488
def mail_to(email_address, name = nil, html_options = {}, &block)
  html_options, name = name, nil if name.is_a?(Hash)
  html_options = (html_options || {}).stringify_keys

  extras = %w{ cc bcc body subject reply_to }.map! { |item|
    option = html_options.delete(item).presence || next
    "#{item.dasherize}=#{ERB::Util.url_encode(option)}"
  }.compact
  extras = extras.empty? ? "" : "?" + extras.join("&")

  encoded_email_address = ERB::Util.url_encode(email_address).gsub("%40", "@")
  html_options["href"] = "mailto:#{encoded_email_address}#{extras}"

  content_tag("a", name || email_address, html_options, &block)
end

phone_to(phone_number, name = nil, html_options = {}, &block)

创建到指定 phone_number 的 TEL 锚点链接标签。点击链接时,将打开默认的电话拨打应用程序,并预填电话号码。

如果未指定 name,则 phone_number 将用作链接的名称。

支持 country_code 选项,该选项会在链接的电话号码前加上加号和指定的国家代码。例如,country_code: "01" 将在链接的电话号码前加上 +01

可以通过 html_options 传递其他 HTML 属性。

Options

  • :country_code - 在电话号码前加上国家代码

Examples

phone_to "1234567890"
# => <a href="tel:1234567890">1234567890</a>

phone_to "1234567890", "Phone me"
# => <a href="tel:1234567890">Phone me</a>

phone_to "1234567890", country_code: "01"
# => <a href="tel:+011234567890">1234567890</a>

如果你的链接目标难以放入 name 参数,你也可以使用块。ERB 示例

<%= phone_to "1234567890" do %>
  <strong>Phone me:</strong>
<% end %>
# => <a href="tel:1234567890">
       <strong>Phone me:</strong>
     </a>
# File actionview/lib/action_view/helpers/url_helper.rb, line 693
def phone_to(phone_number, name = nil, html_options = {}, &block)
  html_options, name = name, nil if name.is_a?(Hash)
  html_options = (html_options || {}).stringify_keys

  country_code = html_options.delete("country_code").presence
  country_code = country_code.nil? ? "" : "+#{ERB::Util.url_encode(country_code)}"

  encoded_phone_number = ERB::Util.url_encode(phone_number)
  html_options["href"] = "tel:#{country_code}#{encoded_phone_number}"

  content_tag("a", name || phone_number, html_options, &block)
end

sms_to(phone_number, name = nil, html_options = {}, &block)

创建到指定 phone_number 的 SMS 锚点链接标签。点击链接时,将打开默认的 SMS 消息应用程序,准备向链接的电话号码发送消息。如果指定了 body 选项,则消息内容将预设为 body

如果未指定 name,则 phone_number 将用作链接的名称。

支持 country_code 选项,该选项会在链接的电话号码前加上加号和指定的国家代码。例如,country_code: "01" 将在链接的电话号码前加上 +01

可以通过 html_options 传递其他 HTML 属性。

Options

  • :country_code - 在电话号码前加上国家代码。

  • :body - 预设消息的正文。

Examples

sms_to "5155555785"
# => <a href="sms:5155555785;">5155555785</a>

sms_to "5155555785", country_code: "01"
# => <a href="sms:+015155555785;">5155555785</a>

sms_to "5155555785", "Text me"
# => <a href="sms:5155555785;">Text me</a>

sms_to "5155555785", body: "I have a question about your product."
# => <a href="sms:5155555785;?body=I%20have%20a%20question%20about%20your%20product">5155555785</a>

如果你的链接目标难以放入 name 参数,你也可以使用块。ERB 示例

<%= sms_to "5155555785" do %>
  <strong>Text me:</strong>
<% end %>
# => <a href="sms:5155555785;">
       <strong>Text me:</strong>
     </a>
# File actionview/lib/action_view/helpers/url_helper.rb, line 642
def sms_to(phone_number, name = nil, html_options = {}, &block)
  html_options, name = name, nil if name.is_a?(Hash)
  html_options = (html_options || {}).stringify_keys

  country_code = html_options.delete("country_code").presence
  country_code = country_code ? "+#{ERB::Util.url_encode(country_code)}" : ""

  body = html_options.delete("body").presence
  body = body ? "?&body=#{ERB::Util.url_encode(body)}" : ""

  encoded_phone_number = ERB::Util.url_encode(phone_number)
  html_options["href"] = "sms:#{country_code}#{encoded_phone_number};#{body}"

  content_tag("a", name || phone_number, html_options, &block)
end