目前,我有几种与嵌套属性一起使用的表单。我最近发现了Dropzone JS,所以我想以各种形式实现它。
我有两个类似的模型:
游戏
class Games < ActiveRecord::base
has_many :screenshots, dependent: :destroy
accepts_nested_attributes_for :screenshots, allow_destroy: true
end
截图
class Screenshot < ActiveRecord::Base
belongs_to :game
mount_uploader :ssfile, ScreenshotUploader
end
通常,我会用这样的东西处理它:
new.html.haml
- content_for :breadcrumbs do
%h3.text-center New Game
.col-md-12#content
.row
= render 'form'
.row
.col-md-12
= link_to 'Back', games_path
_form.html.haml
.col-md-12
= form_for @game, :html => {:class => 'form-horizontal form-bordered'} do |f|
.section-title
%h3 General Data
.well
.form-group
=f.label :title, :class => 'col-md-2'
.col-md-6
= f.text_field :title, :class => 'form-control'
.form-group
= f.label :platform, :class => 'col-md-2'
.col-md-6
.input-group
= f.text_field :platform, :class => 'form-control'
.section-title
%h3 Screenshots
.well#screenshots
=f.fields_for :screenshots do |i|
=render 'screenshot_fields', :f => i
%hr
.links
=link_to_add_association 'Add Screenshot', f, :screenshots, :class => 'btn btn-default'
.row
.col-md-12
.actions
= f.submit 'Save', :class => 'btn btn-default'
_screenshot_fields.html.haml
.form-group
-if f.object.ssfile?
.col-md-2
=image_tag(f.object.ssfile_url(:thumb))
.col-md-4
%p
=f.file_field :ssfile, :class => 'styledfileinput'
-if f.object.ssfile?
.col-md-2
%p
=f.check_box :_destroy
=f.label :_destroy, 'Remove'
这很好用。当您单击“添加屏幕截图”时,它会加载link_to_add_association
帮助程序,它是名为Cocoon的gem的一部分。这个帮助器将创建必要的JS来向表单添加“块”代码,在这种情况下是文件输入,但通常你会有更多其他类型的输入而不是一个。
我想将link_to_add_association
添加的块替换为多文件放置区。
我对new.html.haml
视图进行了以下更改:
.col-md-12
= form_for @game, :html => {:class => 'form-horizontal form-bordered', :id => 'gameform'} do |f|
-# *snip*
.well#screenshots
- @game.screenshots.each do |i|
=image_tag i.ssfile_url(:thumb)
%hr
.row
.col-md-12
.actions
= f.submit 'Save', :class => 'btn btn-default'
我将#gameform
添加到form_for
元素,并替换了显示图片的循环。到现在为止还挺好。现在的问题是我不知道文件应该如何与表单进行交互。
我在加载Dropzone配置的coffeescript上有这个:
ready = ->
if $('#gameform').length > 0
didthiswork = new Dropzone("#gameform", {
paramName: "game[screenshot_attributes][ssfile]"
})
else
console.log('ruh roh')
$(document).on('page:load ready', ready)
现在,我可以点击表单中的任意位置,它会带上传对话框(这本身就是另一个问题),但是一旦我选择了图片,这些图片就没有正确保存。有没有办法让这个工作fields_for
?我有很多其他形式使用非常相似的配置,所以我更喜欢不必更改我的应用程序的整个逻辑(如果可能的话)。
我想我已经取得了一些进展。我已经设法让表单发送数据,但是它们没有到达我的预期。
我使用此链接中的信息调整了配置:https://github.com/enyo/dropzone/wiki/Combine-normal-form-with-Dropzone
在我的coffescript上发生了类似的事情:
Dropzone.options.articuloform =
autoProcessQueue: false
uploadMultiple: true
paramName: "game[screenshot_attributes][ssfile]"
parallelUploads: 100
maxFiles: 100
init: ->
myDropzone = this
# First change the button to actually tell Dropzone to process the queue.
@element.querySelector('input[type=submit]').addEventListener 'click', (e) ->
# Make sure that the form isn't actually being sent.
e.preventDefault()
e.stopPropagation()
myDropzone.processQueue()
return
# Listen to the sendingmultiple event. In this case, it's the sendingmultiple event instead
# of the sending event because uploadMultiple is set to true.
@on 'sendingmultiple', ->
# Gets triggered when the form is actually being sent.
# Hide the success button or the complete form.
return
@on 'successmultiple', (files, response) ->
# Gets triggered when the files have successfully been sent.
# Redirect user or notify of success.
return
@on 'errormultiple', (files, response) ->
# Gets triggered when there was an error sending the files.
# Maybe show form again, and notify user of error
return
return
这样可以防止在选择图像时自动上传数据(这很棒),但是,一旦按下“提交”按钮,数据就会以错误的结构到达。他们在数据中这样到达:
Parameters: {
"utf8"=>"✓",
"authenticity_token"=>"l2siCG2D4xW0eto48WM152MTusc6K5cQYem8K2a9c/Y=",
"game"=>{
"title"=>"Super Amazing AAA Title",
"platform"=>"Xbox One",
"screenshot_attributes"=>{
"ssfile"=>{
"0"=>#<ActionDispatch::Http::UploadedFile:0x000000055c7e98 @tempfile=#<Tempfile:/tmp/RackMultipart20150408-9276-14rfzqm>,
@original_filename="screenshot01.png",
@content_type="image/png",
@headers="Content-Disposition: form-data; name=\"game[screenshot_attributes][ssfile][0]\"; filename=\"screenshot01.png\"\r\nContent-Type: image/png\r\n">,
"1"=>#<ActionDispatch::Http::UploadedFile:0x000000055c7df8 @tempfile=#<Tempfile:/tmp/RackMultipart20150408-9276-1opclgk>,
@original_filename="screenshot02.png",
@content_type="image/png",
@headers="Content-Disposition: form-data; name=\"game[screenshot_attributes][ssfile][1]\"; filename=\"screenshot02.png\"\r\nContent-Type: image/png\r\n">,
"2"=>#<ActionDispatch::Http::UploadedFile:0x000000055c7d30 @tempfile=#<Tempfile:/tmp/RackMultipart20150408-9276-16r4kdq>,
@original_filename="screenshot03.png",
@content_type="image/png",
@headers="Content-Disposition: form-data; name=\"game[screenshot_attributes][ssfile][2]\"; filename=\"screenshot03.png\"\r\nContent-Type: image/png\r\n">,
"3"=>#<ActionDispatch::Http::UploadedFile:0x000000055c7c90 @tempfile=#<Tempfile:/tmp/RackMultipart20150408-9276-qp1au3>,
@original_filename="screenshot04.png",
@content_type="image/png",
@headers="Content-Disposition: form-data; name=\"game[screenshot_attributes][ssfile][3]\"; filename=\"screenshot04.png\"\r\nContent-Type: image/png\r\n">,
"4"=>#<ActionDispatch::Http::UploadedFile:0x000000055c7ba0 @tempfile=#<Tempfile:/tmp/RackMultipart20150408-9276-8g3pap>,
@original_filename="screenshot05.png",
@content_type="image/png",
@headers="Content-Disposition: form-data; name=\"game[screenshot_attributes][ssfile][4]\"; filename=\"screenshot05.png\"\r\nContent-Type: image/png\r\n">
}
}
},
"null"=>"",
"commit"=>"Save",
"id"=>"2"
}
重要的部分是在每个细分的name
中,它们以game[screenshot_attributes][ssfile][XXXX]
到达,这是错误的,正确的格式应该是game[screenshot_attributes][XXXX][ssfile]
。我可以在上面粘贴的部分中对此进行配置,但重要的部分是paramName
部分,我将其作为paramName: "game[screenshot_attributes][ssfile]"
。
如果有人能有任何线索,我会非常感激。
答案 0 :(得分:2)
最后找到了一种让dropzone.js使用rails嵌套属性的方法。您必须编辑dropzone.js以设置多个附件的rails嵌套属性数组的格式。
#include <iostream>
using namespace std;
class Name{
public:
void Fun(){
cout << "Function" << endl;
}
Name(){
cout << "Constructor" << endl;
}
~Name(){
cout << "Destructor" << endl;
}
};
int main(){
Name obj;
obj.Fun();
cout << "End" << endl;
return 0;
}
然后在dropzone初始化javascript初始化两个参数:
Dropzone.prototype._getParamName = function(n) {
if (typeof this.options.paramName === "function") {
return this.options.paramName(n);
} else {
return "" + this.options.paramName1 + (this.options.uploadMultiple ? "[" + n + "]" : "") + this.options.paramName2 + "";
}
};