Ruby on Rails - 视图
Ruby on Rails - 视图
Section titled “Ruby on Rails - 视图”在 Rails 中,视图(Views)是 MVC 中的 ‘V’。它们负责生成用户界面,通常是 HTML。Rails 视图最常使用 ERB (Embedded Ruby) 编写,它允许您在 HTML 文档中嵌入 Ruby 代码。数据通过实例变量(instance variables)从控制器(controllers)传递到视图(views)。
当您生成一个控制器时,Rails 通常会在 app/views/ 中创建一个相应的子目录。例如,如果您有一个 BooksController,其视图文件将位于 app/views/books/。每个公共的控制器动作(action)通常都有一个对应的视图模板文件(例如,index 动作对应 index.html.erb)。
让我们为 BooksController 中定义的动作创建视图文件。在构建这些视图时,请思考数据如何从控制器流向视图,以及 Rails 助手方法(helpers)如何简化 HTML 生成。
为 index 动作创建视图(图书列表)
Section titled “为 index 动作创建视图(图书列表)”在 app/views/books/ 中创建一个名为 index.html.erb 的文件。这个视图将显示所有图书的列表。如果目前没有图书,它将显示一条消息。
将以下 ERB 代码添加到 app/views/books/index.html.erb 文件中:
<h1>Available Books</h1>
<% if @books.empty? %> <p>There are no books currently in the system.</p><% else %> <p>These are the current books in our system:</p> <ul id="books"> <% @books.each do |book| %> <li id="book_<%= book.id %>"> <%= link_to book.title, book_path(book) %> <%= link_to 'Edit', edit_book_path(book) %> <%= link_to 'Delete', book_path(book), data: { turbo_method: :delete, turbo_confirm: 'Are you sure you want to delete this item?' } %> </li> <% end %> </ul><% end %>
<p><%= link_to "Add new Book", new_book_path %></p>解释:
@books.empty?检查实例变量@books(在控制器的index动作中设置)是否为空。@books.each do |book| ... end遍历每本图书。link_to book.title, book_path(book)创建一个指向图书详情页的链接。book_path(book)是一个路由助手方法(route helper),它为给定的book对象生成books#show对应的 URL。edit_book_path(book)链接到图书的编辑页。link_to 'Delete', book_path(book), data: { turbo_method: :delete, turbo_confirm: 'Are you sure you want to delete this item?' }创建一个删除链接。现代 Rails 使用 Turbo 来处理此类动作,需要设置turbo_method: :delete。turbo_confirm提供一个 JavaScript 确认对话框。new_book_path链接到创建新图书的页面。
如果您在浏览器中访问 /books 并且尚未添加任何图书,您将看到消息 “There are no books currently in the system.” 以及一个添加新图书的链接。
为 new 动作创建视图(新图书表单)
Section titled “为 new 动作创建视图(新图书表单)”要添加图书,我们需要一个表单(form)。在 app/views/books/ 中创建 new.html.erb 文件。
将以下代码添加到 app/views/books/new.html.erb 文件中:
<h1>Add New Book</h1>
<%= form_with(model: @book, local: true) do |form| %> <% if @book.errors.any? %> <div id="error_explanation"> <h2><%= pluralize(@book.errors.count, "error") %> prohibited this book from being saved:</h2> <ul> <% @book.errors.full_messages.each do |message| %> <li><%= message %></li> <% end %> </ul> </div> <% end %>
<p> <%= form.label :title %><br> <%= form.text_field :title %> </p>
<p> <%= form.label :price %><br> <%= form.number_field :price, step: 0.01 %> </p>
<p> <%= form.label :subject_id %><br> <%= form.collection_select :subject_id, @subjects, :id, :name, prompt: 'Select a Subject' %> </p>
<p> <%= form.label :description %><br> <%= form.text_area :description %> </p>
<p> <%= form.submit 'Create Book' %> </p><% end %>
<%= link_to 'Back to List', books_path %>解释:
form_with(model: @book, local: true) do |form| ... end是现代 Rails 的表单构建器(form builder)。model: @book告诉它为@book对象(在控制器的new动作中实例化)构建一个表单。local: true确保默认情况下执行标准的 HTML 表单提交,而不是 AJAX 提交。- 错误显示块会在保存失败时显示验证错误(validation errors)。
form.label、form.text_field、form.number_field、form.collection_select、form.text_area和form.submit都是表单助手方法(form helper methods),用于生成相应的 HTML 表单元素。collection_select用于 ‘Subject’ 下拉列表,由@subjects(从控制器传递过来)填充数据。- 导航到
/books/new将显示这个表单。
填写完表单并点击 “Create Book” 后,数据将提交到 create 动作。如果成功,您将被重定向到图书列表页,现在列表将显示新添加的图书。如果存在验证错误,new 页面将重新渲染并显示错误消息。
为 show 动作创建视图(显示图书详情)
Section titled “为 show 动作创建视图(显示图书详情)”此视图显示特定图书的详细信息。在 app/views/books/ 中创建 show.html.erb 文件。
添加以下代码:
<h1><%= @book.title %></h1>
<p> **Price:** <%= number_to_currency(@book.price) %><br> **Subject:** <%= @book.subject&.name || 'N/A' %><br> **Created Date:** <%= @book.created_at.strftime("%B %d, %Y") %><br></p>
<p> **Description:**<br> <%= @book.description %></p>
<hr>
<%= link_to 'Edit this Book', edit_book_path(@book) %> |<%= link_to 'Back to List', books_path %>解释:
@book.title、@book.price等显示控制器show动作找到的图书的属性。number_to_currency是一个 Rails 助手方法(helper),用于格式化价格。@book.subject&.name使用 Ruby 的安全导航操作符(safe navigation operator&.) 来防止图书没有关联主题时发生错误。然后它会显示主题名称或 ‘N/A’。strftime格式化created_at时间戳。- 提供了编辑图书或返回列表的链接。
为 edit 动作创建视图(编辑图书表单)
Section titled “为 edit 动作创建视图(编辑图书表单)”这个表单类似于 new 表单,但会预填充图书的现有数据,并提交到 update 动作。在 app/views/books/ 中创建 edit.html.erb 文件。
添加以下代码:
<h1>Edit Book Details</h1>
<%= form_with(model: @book, local: true) do |form| %> <% if @book.errors.any? %> <div id="error_explanation"> <h2><%= pluralize(@book.errors.count, "error") %> prohibited this book from being saved:</h2> <ul> <% @book.errors.full_messages.each do |message| %> <li><%= message %></li> <% end %> </ul> </div> <% end %>
<p> <%= form.label :title %><br> <%= form.text_field :title %> </p>
<p> <%= form.label :price %><br> <%= form.number_field :price, step: 0.01 %> </p>
<p> <%= form.label :subject_id %><br> <%= form.collection_select :subject_id, @subjects, :id, :name, prompt: 'Select a Subject' %> </p>
<p> <%= form.label :description %><br> <%= form.text_area :description %> </p>
<p> <%= form.submit 'Update Book' %> </p><% end %>
<%= link_to 'Show this Book', book_path(@book) %> |<%= link_to 'Back to List', books_path %>这个表单与 new 表单几乎完全相同。form_with(model: @book) 会自动设置表单的提交动作到 PATCH /books/:id(即 update 动作),并且因为 @book 是一个已存在的记录,会自动预填充字段。
delete 动作的视图
Section titled “delete 动作的视图”delete(或 destroy)动作通常没有自己的视图。删除记录后,控制器通常会重定向到 index 页面。删除功能是通过 index 页面(或 show 页面)上的链接触发的,如前面所示。
为 show_subjects 动作创建视图(自定义动作示例)
Section titled “为 show_subjects 动作创建视图(自定义动作示例)”如果您在 BooksController 中有一个像 show_subjects 这样的自定义动作(也许是为了按特定主题列出图书,尽管这种逻辑通常应放在 SubjectsController 中),您需要创建 app/views/books/show_subjects.html.erb 文件。
假设 @subject 及其关联的 @books 已在控制器中设置:
<h1>Books in Subject: <%= @subject.name %></h1>
<% if @subject.books.empty? %> <p>No books found for this subject.</p><% else %> <ul> <% @subject.books.each do |book| %> <li><%= link_to book.title, book_path(book) %></li> <% end %> </ul><% end %>
<%= link_to 'Back to All Books', books_path %>这演示了如何遍历与特定主题关联的图书。您还需要为此设置相应的路由(route)和控制器动作(controller action)。例如,在 show.html.erb 中,您可以在主题名称处链接到此处:
**Subject:** <%= link_to @book.subject&.name, subject_books_path(@book.subject) if @book.subject %>(假设存在一个路由助手方法 subject_books_path)。
要在主图书列表页(app/views/books/index.html.erb)上列出所有主题,您可以添加(假设 @all_subjects 从控制器传递过来):
<h2>Browse by Subject</h2><ul id="subjects"> <% @all_subjects.each do |subject| %> <li><%= link_to subject.name, books_path(subject_id: subject.id) %></li> <%# Or link_to subject.name, subject_path(subject) if you have a subjects#show page %> <% end %></ul>这个例子展示了一种使用查询参数(query parameters)按主题过滤图书列表的方法。然后控制器的 index 动作需要处理这个参数。
下一步是什么?
Section titled “下一步是什么?”有了处理逻辑的控制器和展示数据的视图,您就拥有了一个 Web 应用的核心。下一章将探讨布局(Layouts),它为多个视图提供了统一的结构和外观。