从根本上说,我试图将几个默认的 rails 视图合并为一个,并使用 javascript(实际上是 coffeescript)来避免重新加载页面。基本上我想结合标准生成的新函数和索引函数中的内容,以便在创建新对象时更新列表而无需刷新页面。
我想这是许多应用程序中的常用技术,尽管我还没有找到关于它的教程。我已经尝试从 StackOverflow 和其他地方的帮助中拼凑出一个解决方案,但还没有完全发挥作用。我决定创建一个简单的 Rails 项目,因为我需要帮助来解决这个问题,因此其他人可以跟进,而不会陷入特定于我的应用程序的事情中。
下面是代码。有很多,但大部分都是标准的,由 rails 生成。我创建了一个 rails 项目(rails 3.2)并为具有两个字符串、一个标题和一个作者的 Book 生成了脚手架(模型、控制器、视图)。然后我进一步修改了代码如下。
我确保Gemfile
包含jquery-rails
gem 'rails', '3.2.7'
....
group :assets do
gem 'sass-rails', '~> 3.2.3'
gem 'coffee-rails', '~> 3.2.1'
gem 'uglifier', '>= 1.0.3'
end
gem 'jquery-rails'
然后我为列表操作添加了一条新路线Routes.rb
...
match 'books/list' => 'books#list'
resources :books
接下来将动作添加到控制器books_controller.rb
...
def list
@all_books = Book.all
@book = Book.new
respond_to do |format|
format.html # new.html.erb
format.js { render 'new_book_row', :format => :html, :layout => false }
format.json { render json: @book }
end
end
我确保同时拥有@book
新书和@all_books
用于列出所有书籍的表格。
该format.js
行应将新表行(下面给出的文件)呈现为 html(将布局设置为 false,因此它只呈现部分而不是应用程序视图包装器)。
我们在中创建相应的视图和局部视图views/books/
list.html.erb
包含
<h1>List Books</h1>
<h3>Add Book</h3>
<%= render 'new_book_form' %>
<h3>My Books</h3>
<%= render 'book_list' %>
<%= link_to 'Back', books_path %>
我把它分成了两个部分,尽管显然这些部分可以折叠到上面的视图中。
部分_new_book_form.html.erb
如下
<%= form_for @book, :remote => true, :html => { 'data-type' => :html } do |f| %>
<% 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 |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :title %><br />
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :author %><br />
<%= f.text_field :author %>
</div>
<div class="add-button" id='add-button'>
<%= f.submit %>
</div>
<% end %>
除了 form_for 调用之外,它都是标准生成的代码。我按照http://tech.thereq.com/post/18910961502/rails-3-using-form-remote-true-with-jquery-ujs:remote => true
中的描述进行设置,使其成为 ajax 调用。我需要进行设置,以便响应以 html 而不是脚本的形式出现。'data-type' => :html
_book_list.html.erb
部分也是所有标准生成的代码
<table id='book-list'>
<tr>
<th>Title</th>
<th>Author</th>
<th></th>
<th></th>
<th></th>
</tr>
<% @all_books.each do |book| %>
<tr>
<td><%= book.title %></td>
<td><%= book.author %></td>
<td><%= link_to 'Show', book %></td>
<td><%= link_to 'Edit', edit_book_path(book) %></td>
<td><%= link_to 'Destroy', book, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</table>
也_new_book_row.html.erb
很简单
<tr>
<td><%= new_book.title %></td>
<td><%= new_book.author %></td>
<td><%= link_to 'Show', new_book %></td>
<td><%= link_to 'Edit', edit_book_path(new_book) %></td>
<td><%= link_to 'Destroy', new_book, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
魔法就在books.js.coffee
# don't load javascript until the whole page is laid out
$(document).ready ->
# To save the book without reloading the page
$('#add-button').click ->
$(this).parents("form").submit()
$('form#new_book').on 'ajax:success', (xhr, data, status) ->
$('#book-list').append(data).show()
我发现我需要延迟添加加载 javascript,直到页面加载完成,否则找不到元素(当你想到它时,这并不奇怪)。
/books/list
正确显示页面,并且按钮确实创建了带有表单字段的 Book 模型的新实例,而不呈现默认/books/<id>
页面。
然而
- 这不是刷新表。
- 它似乎正在创建对象的两个实例。
- 它没有渲染部分(我也尝试过不带前导下划线的非部分),而是返回 html
/books/<id>
(但再次执行两次),如下所示。
Dynamicdisplay ... ... 重新加载页面以获取源: /assets/books.css?body=1 ... ... 图书已成功创建。
书名: 老人与海
作者: 海明威
编辑 | 返回 Dynamicdisplay ... ... 图书创建成功。书名: 老人与海
作者: 海明威
编辑 | 后退我想我已经接近了,我只需要获取 javascript 来获取正确的部分并在正确的位置更新 DOM。
谢谢你的帮助。