在 Rails 5.1 中预填充嵌套表单时 HTML 不正确
Incorrect HTML when pre-populating nested forms in Rails 5.1
我有一个使用 Google 书籍 API 的 Rails 5.1 应用程序,我需要在表单中预填充嵌套字段。有两种方法可以创建 Book
。
首先,通过正常的 /books/new
形式,accepts_nested_attributes_for :authors
与 has_many: through
关联。我在这里使用 cocoon
gem 并且一切正常。
其次,用户可以通过使用 ISBN 号搜索 Google 本书 API 来创建 Book
。然后,此数据会在提交给 BooksController
中的 create
操作之前预填充表单。除了能够正确提交嵌套的 Author
数据之外,我已经成功地完成了这项工作。
我目前让每个作者在表单中填充一个字段,但是当我提交数据时,只有作者数组中的最后一项(对于有多位作者的书)被保存。
我认为这是因为表单 html 的两个字段具有相同的 name
和 id
,如下所示。我如何获得此表单以提交两位作者?
<input value="John J. Ratey" type="text" name="book[authors_attributes][0][name]" id="book_authors_attributes_0_name">
<input value="Richard Manning" type="text" name="book[authors_attributes][0][name]" id="book_authors_attributes_0_name">
books_controller.rb
class BooksController < ApplicationController
before_action :authenticate_user!
before_action :set_book, except: [:index, :new, :create, :new_book, :submit_book]
def create
@book = current_user.books.create(book_params)
@book.authors.each {|author| author.user_id = current_user.id}
if @book.save
redirect_to book_path(@book)
else
render :new
end
end
def new_book
end
def submit_book
@book = Book.new
@book.authors.new
@response = GoogleBooks.new(params[:q], @book)
end
private
def set_book
@book = Book.find(params[:id])
end
def book_params
params.require(:book).permit(:title, :subtitle, :description, author_ids:[], authors_attributes: [:id, :name, :_destroy])
end
end
book.rb
class Book < ApplicationRecord
has_many :book_authors
has_many :authors, through: :book_authors
accepts_nested_attributes_for :authors, allow_destroy: true
validates :title, presence: true
validates_associated :authors
end
google_books.rb
class GoogleBooks
include HTTParty
base_uri 'https://www.googleapis.com/books/v1'
def initialize(isbn, book)
@query = self.class.get("/volumes?q=isbn:#{isbn}")
@book = book
end
def title
@query['items'].first['volumeInfo']['title']
end
def subtitle
@query['items'].first['volumeInfo']['subtitle']
end
def description
@query['items'].first['volumeInfo']['description']
end
def authors
@query['items'].first['volumeInfo']['authors']
#=> ['John J. Ratey', 'Richard Manning']
end
end
submit_book.html.erb
<%= form_for @book do |f| %>
<%= f.text_field :title, value: @response.title %>
<%= f.text_field :subtitle, value: @response.subtitle %>
<%= f.text_field :description, value: @response.description %>
<%= f.fields_for :authors, @book.authors.build do |authors_fields| %>
<% @response.authors.each do |author| %>
<%= authors_fields.text_field :name, value: author %>
<% end %>
<% end %>
<%= f.submit %>
解决了。
是一个改变的案例
<%= f.fields_for :authors, @book.authors.build do |authors_fields| %>
<% @response.authors.each do |author| %>
<%= authors_fields.text_field :name, value: author %>
<% end %>
<% end %>
至此
<% @response.authors.each do |author| %>
<%= f.fields_for :authors, @book.authors.build do |authors_fields| %>
<%= authors_fields.text_field :name, value: author %>
<% end %>
<% end %>
产生以下正确的HTML:
<input value="John J. Ratey" type="text" name="book[authors_attributes][0][name]" id="book_authors_attributes_0_name">
<input value="Richard Manning" type="text" name="book[authors_attributes][1][name]" id="book_authors_attributes_1_name">
我有一个使用 Google 书籍 API 的 Rails 5.1 应用程序,我需要在表单中预填充嵌套字段。有两种方法可以创建 Book
。
首先,通过正常的 /books/new
形式,accepts_nested_attributes_for :authors
与 has_many: through
关联。我在这里使用 cocoon
gem 并且一切正常。
其次,用户可以通过使用 ISBN 号搜索 Google 本书 API 来创建 Book
。然后,此数据会在提交给 BooksController
中的 create
操作之前预填充表单。除了能够正确提交嵌套的 Author
数据之外,我已经成功地完成了这项工作。
我目前让每个作者在表单中填充一个字段,但是当我提交数据时,只有作者数组中的最后一项(对于有多位作者的书)被保存。
我认为这是因为表单 html 的两个字段具有相同的 name
和 id
,如下所示。我如何获得此表单以提交两位作者?
<input value="John J. Ratey" type="text" name="book[authors_attributes][0][name]" id="book_authors_attributes_0_name">
<input value="Richard Manning" type="text" name="book[authors_attributes][0][name]" id="book_authors_attributes_0_name">
books_controller.rb
class BooksController < ApplicationController
before_action :authenticate_user!
before_action :set_book, except: [:index, :new, :create, :new_book, :submit_book]
def create
@book = current_user.books.create(book_params)
@book.authors.each {|author| author.user_id = current_user.id}
if @book.save
redirect_to book_path(@book)
else
render :new
end
end
def new_book
end
def submit_book
@book = Book.new
@book.authors.new
@response = GoogleBooks.new(params[:q], @book)
end
private
def set_book
@book = Book.find(params[:id])
end
def book_params
params.require(:book).permit(:title, :subtitle, :description, author_ids:[], authors_attributes: [:id, :name, :_destroy])
end
end
book.rb
class Book < ApplicationRecord
has_many :book_authors
has_many :authors, through: :book_authors
accepts_nested_attributes_for :authors, allow_destroy: true
validates :title, presence: true
validates_associated :authors
end
google_books.rb
class GoogleBooks
include HTTParty
base_uri 'https://www.googleapis.com/books/v1'
def initialize(isbn, book)
@query = self.class.get("/volumes?q=isbn:#{isbn}")
@book = book
end
def title
@query['items'].first['volumeInfo']['title']
end
def subtitle
@query['items'].first['volumeInfo']['subtitle']
end
def description
@query['items'].first['volumeInfo']['description']
end
def authors
@query['items'].first['volumeInfo']['authors']
#=> ['John J. Ratey', 'Richard Manning']
end
end
submit_book.html.erb
<%= form_for @book do |f| %>
<%= f.text_field :title, value: @response.title %>
<%= f.text_field :subtitle, value: @response.subtitle %>
<%= f.text_field :description, value: @response.description %>
<%= f.fields_for :authors, @book.authors.build do |authors_fields| %>
<% @response.authors.each do |author| %>
<%= authors_fields.text_field :name, value: author %>
<% end %>
<% end %>
<%= f.submit %>
解决了。
是一个改变的案例
<%= f.fields_for :authors, @book.authors.build do |authors_fields| %>
<% @response.authors.each do |author| %>
<%= authors_fields.text_field :name, value: author %>
<% end %>
<% end %>
至此
<% @response.authors.each do |author| %>
<%= f.fields_for :authors, @book.authors.build do |authors_fields| %>
<%= authors_fields.text_field :name, value: author %>
<% end %>
<% end %>
产生以下正确的HTML:
<input value="John J. Ratey" type="text" name="book[authors_attributes][0][name]" id="book_authors_attributes_0_name">
<input value="Richard Manning" type="text" name="book[authors_attributes][1][name]" id="book_authors_attributes_1_name">