这篇文章是Hotwire Summer的一部分:Boring Rails的新一季内容!
Rails中的dom_id 帮助器已有十多年历史,但在Hotwire中被证明是一个非常有价值的概念。
这个秘密的主力军为Rails中各种与HTML相关的行为提供动力。它有一项关键工作:使应用程序数据与DOM元素的关联变得容易。
dom_id 它需要两个参数:一个记录和一个可选的前缀。
record 可以是任何响应to_key 和model_name 的东西,但99%的情况下,你会传递给它一个 ActiveRecord 模型。prefix 可以是任何响应to_s 的东西,但99%的情况下它是一个符号。
来自文档。
dom_id(Post.find(45)) # => "post_45"
dom_id(Post.new) # => "new_post"
dom_id(Post.find(45), :edit) # => "edit_post_45"
dom_id(Post.new, :custom) # => "custom_post"
这个助手之所以这么好,是因为它设定了一个惯例。Rails提供了一个标准,而不是定义你自己的标识方式,然后希望你记住这个模式;你不需要切换上下文来知道你是否将一个id设置为 "post_23_comments "或 "comments-post-23",或者等等,是 "post_23-comments"?
通过为你的元素提供稳定的id值,你可以避免脆弱的代码,比如针对第一个元素的孩子或根据文本值找到一个节点。
以下是在构建Hotwire应用程序时,你应该经常接触的地方dom_id 。
超级简洁的标签构建器
将HTML标记作为真理的来源意味着在渲染模板时要添加额外的属性和条件。虽然你可以在ERB模板中进行普通的字符串插值,但Rails有一套漂亮的标签生成器,可以帮助你避免淹没在大括号、小括号和八边形的海洋中。
<%= tag.div id: dom_id(@post, :comments), class: "flex flex-col divide-y" do %>
<%= render @post.comments %>
<% end %>
当你更多地使用这些辅助工具时,请确保你查看这些提示。
- 为VS Code设置Tailwind Intellisense插件,以便在使用标签助手时自动完成。
- 利用class_name帮助器(来自
classNamesReact API)来获得条件类。 - 对于commmon元素,考虑用一个轻量级的帮助器或像ViewComponent这样的库来提取一个组件
深度链接锚点标签
由于Rails非常倚重本地浏览器的功能,所以要确保你利用链接上的锚定标签。你可以使用dom_id ,让浏览器直接滚动到具有相应id 的元素(或者生成一个用户可以分享的固定链接)。
<%= link_to "View comment", posts_path(@post, anchor: dom_id(@comment)) %>
你也可以使用这种模式进行重定向。例如,在一个列表中创建一个项目后,你可以重定向到索引页,但滚动到新的项目。
class CommentsController < ApplicationController
def create
@post.comments.create!(comment_params)
redirect_to posts_path(@post, anchor: dom_id(@comment))
end
end
还有一些与深度链接有关的提示。
- 你可以使用
:target伪类,用一个与URL锚匹配的ID来设计元素。在Tailwind中,只需使用目标前缀(例如:target:bg-yellow-50,当元素与URL锚匹配时,为其添加一个微妙的黄色背景) - 另一个方便的CSS属性是
scroll-margin-top:浏览器将把目标元素一直滚动到窗口的顶部。你可能想要一点额外的填充,但只有当你滚动到该元素时才需要。不要在你的设计中添加额外的边距或填充,也不要添加奇怪的包装div......scroll-margin-top(Tailwind class:scroll-mt)就是答案。
使用Turbo Frames
当你开始在你的应用程序中添加Turbo Frames时,你需要为框架标签提供一个id。Railsturbo_frame_tag 使用 - 你猜对了 -dom_id 在引擎盖下。但你也可以传入你自己的id。
turbo_frame_tag @post # => <turbo-frame id="post_123"></turbo-frame>
turbo_frame_tag dom_id(@post, :comments) # => <turbo-frame id="comments_post_123"></turbo-frame>
由于Turbo Frame需要在每个页面上都是唯一的,所以dom_id 是一种生成框架id的方便方法,特别是当你在页面上有多个框架时。
而且,由于你可以通过其他链接来浏览一个框架,这是一个很好的约定。
<%= turbo_frame_tag @comment, src: comment_path(@comment) %>
<!-- Elsewhere... -->
<%= link_to "Edit", edit_comment_path(@comment), data: { turbo_frame: @comment } %>
对Turbo Stream响应进行范围界定
用Turbo Streams在页面上做一些小的突变是非常强大的,但是由于你的流动作是在一个单独的turbo_stream.erb 文件中,所以在不同的视图中匹配你的id是很困难的。
再一次,dom_id 可以保持一致性。
<!-- app/views/plans/quick_edit/update.turbo_stream.erb -->
<%= turbo_stream.replace dom_id(@plan, :title), partial: "plans/title" %>
<%= turbo_stream.replace dom_id(@plan, :notes), partial: "plans/notes" %>
<%= turbo_stream.replace dom_id(@plan, :assigned), partial: "plans/assigned" %>
通过在你的标记中添加正确的ID,Turbo Stream的响应是非常干净的。
<!-- app/views/comments/destroy.turbo_stream.erb -->
<!-- Calls `dom_id(@comment)` under the hood -->
<%= turbo_stream.remove @comment %>
收尾工作
谁能想到,从你的应用程序模型中生成HTMLid 值的简单助手会是一个如此有用的概念,以至于在首次引入十多年后,它仍然被证明是有用的,甚至在Rails的最新和最闪亮的部分。
如果你以前没有使用过dom_id ,那么下次在你的Rails应用中编写视图时,可以考虑一下。你可能会惊讶地发现,当你不在HTML上乱写乱画时,创建HTML的过程是多么的愉快。
<div id='<%= "#{@post.id}-comments" %>'>
</div>