跳至内容 跳至搜索

Action View Record Identifier

RecordIdentifier 封装了各种 ActionView 辅助方法用于将记录与 DOM 元素关联的方法。

例如,考虑以下代码,它以帖子的形式

<%= form_with(model: post) do |f| %>
  <%= f.text_field :body %>
<% end %>

post 是一个新的、未保存的 ActiveRecord::Base 实例时,生成的 HTML 是

<form class="new_post" id="new_post" action="/posts" accept-charset="UTF-8" method="post">
  <input type="text" name="post[body]" id="post_body" />
</form>

post 是一个已持久化的 ActiveRecord::Base 实例时,生成的 HTML 是

<form class="edit_post" id="edit_post_42" action="/posts/42" accept-charset="UTF-8" method="post">
  <input type="text" value="What a wonderful world!" name="post[body]" id="post_body" />
</form>

在这两种情况下,包装 DOM 元素的 idclass 都是自动生成的,遵循 RecordIdentifier 方法 dom_iddom_class 封装的命名约定。

dom_id(Post)             # => "new_post"
dom_class(Post)          # => "post"
dom_id(Post.new)         # => "new_post"
dom_class(Post.new)      # => "post"
dom_id(Post.find 42)     # => "post_42"
dom_class(Post.find 42)  # => "post"

请注意,这些方法并不严格要求 PostActiveRecord::Base 的子类。任何 Post 类都可以工作,只要其实例响应 to_keymodel_name,并且 model_name 响应 param_key。例如:

class Post
  attr_accessor :to_key

  def model_name
    OpenStruct.new param_key: 'post'
  end

  def self.find(id)
    new.tap { |post| post.to_key = [id] }
  end
end
方法
D
R

常量

JOIN = "_"
 
NEW = "new"
 

实例公共方法

dom_class(record_or_class, prefix = nil)

DOM 类约定是使用对象或类的单数形式。

dom_class(post)   # => "post"
dom_class(Person) # => "person"

如果您需要在同一视图中引用同一类的多个实例,您可以为 dom_class 添加前缀。

dom_class(post, :edit)   # => "edit_post"
dom_class(Person, :edit) # => "edit_person"
# File actionview/lib/action_view/record_identifier.rb, line 78
def dom_class(record_or_class, prefix = nil)
  singular = model_name_from_record_or_class(record_or_class).param_key
  prefix ? "#{prefix}#{JOIN}#{singular}" : singular
end

dom_id(record_or_class, prefix = nil)

DOM id 约定是使用对象或类的单数形式,后面跟着一个下划线。如果没有找到 id,则用“new_”作为前缀。

dom_id(Post.find(45)) # => "post_45"
dom_id(Post)          # => "new_post"

如果您需要在同一视图中引用同一类的多个实例,您可以为 dom_id 添加前缀。

dom_id(Post.find(45), :edit) # => "edit_post_45"
dom_id(Post, :custom)        # => "custom_post"
# File actionview/lib/action_view/record_identifier.rb, line 93
def dom_id(record_or_class, prefix = nil)
  raise ArgumentError, "dom_id must be passed a record_or_class as the first argument, you passed #{record_or_class.inspect}" unless record_or_class

  record_id = record_key_for_dom_id(record_or_class) unless record_or_class.is_a?(Class)
  if record_id
    "#{dom_class(record_or_class, prefix)}#{JOIN}#{record_id}"
  else
    dom_class(record_or_class, prefix || NEW)
  end
end

dom_target(*objects)

DOM target 约定是将任意数量的参数连接成一个字符串。记录会通过 dom_id 处理,而字符串和符号则会保留。

dom_target(Post.find(45))                  # => "post_45"
dom_target(Post.find(45), :edit)           # => "post_45_edit"
dom_target(Post.find(45), :edit, :special) # => "post_45_edit_special"
dom_target(Post.find(45), Comment.find(1)) # => "post_45_comment_1"
# File actionview/lib/action_view/record_identifier.rb, line 111
def dom_target(*objects)
  objects.map! do |object|
    case object
    when Symbol, String
      object
    when Class
      dom_class(object)
    else
      dom_id(object)
    end
  end
  objects.join(JOIN)
end

实例私有方法

record_key_for_dom_id(record)

返回适合用在 HTML DOM id 中的键属性的字符串表示形式。如果需要自定义默认生成的字符串表示,可以覆盖此方法。如果您需要从 dom_id 中读回一个键以查询底层数据库记录,您应该编写一个类似 ‘person_record_from_dom_id’ 的辅助方法,该方法将根据默认实现(它仅用 ‘_’ 连接所有键属性)或您自己的覆盖版本来提取键。默认情况下,此实现会将键字符串传递给一个方法,该方法会将无效 DOM id 的字符替换为有效的字符。如果您覆盖此方法,您需要自己确保您的 dom ids 是有效的。

# File actionview/lib/action_view/record_identifier.rb, line 134
def record_key_for_dom_id(record) # :doc:
  key = convert_to_model(record).to_key
  key && key.all? ? key.join(JOIN) : nil
end