##経緯
Railsアプリケーションで画像を投稿するフォームのプレビューを実装したいと思い、ググったjqueryのサンプルを少しいじって利用させてもらったところ、フォームを送信しても正常に反映されなくてハマったのでメモ
##原因
name属性を書き換えてしまっていたため。
##やったこと
実際関係あったのは一行だけですが一応、修正前のコード全体は
<%= form_with model: @post, local: true do |f| %>
<div class="mt-3">
<div class="form-group">
<label for="file">画像</label>
<div id="file" class="input-group">
<div class="custom-file">
<%= f.file_field :image, id: 'cutomfile',
class: 'custom-file-input', name: 'cutomfile[]' %>
<label class="custom-file-label" for="cutomfile" data-browse="参照">ファイル選択...</label>
</div>
<div class="input-group-append">
<button type="button" class="btn btn-outline-secondary reset">取消</button>
</div>
</div>
</div>
<div class="form-group">
<%= f.label :body, 'ひとこと' %>
<%= f.text_area :body, class: 'form-control' %>
</div>
<div class="nav justify-content-end mt-3">
<%= f.submit '送信', id: 'post-form', class: 'btn btn-primary' %>
</div>
</div>
<% end %>
$(document).on('turbolinks:load', function(){
$('.custom-file-input').on('change', handleFileSelect);
function handleFileSelect(evt) {
// 繰り返し実行時の処理
$('#preview').remove();
$(this).parents('.input-group').before('<div id="preview"></div>');
var file = evt.target.files[0];
var reader = new FileReader();
reader.onload = (function(theFile) {
return function(e) {
if (theFile.type.match('image.*')) {
// 画像のプレビューとファイル名の表示
var $html = [
'<div class="d-inline-block mr-1 mt-1"><img class="img-thumbnail" src="', e.target.result,'" title="', escape(theFile.name), '" style="height:400px;" /><div class="small text-muted text-center">', escape(theFile.name),'</div></div>'
].join('');
}
$('#preview').append($html);
};
})(file);
reader.readAsDataURL(file);
$(this).next('.custom-file-label').html(+ evt.target.files.length + '個のファイルを選択しました');
}
//ファイルの取消
$('.reset').click(function(){
$(this).parent().prev().children('.custom-file-label').html('ファイル選択...');
$('#preview').remove();
$('.custom-file-input').val('');
})
});
最初はJqueryの問題かと思い、queryやtarbolinkについて調べてみるが治らない...
デベロッパーツールやターミナルのログで送信を見てみるとImage以外は正常に送られているので、一度余分な属性をすべて消して出力を見比べてみると
デフォルト
<%= f.file_field :image %>
<!-- 出力 -->
<input type="file" name="post[image]" id="post_image" />
自身で変更したコード
<%= f.file_field :image, id: 'cutomfile', class: 'custom-file-input', name: 'cutomfile[]' %>
<!-- 出力 -->
<input id="cutomfile" class="custom-file-input" name="cutomfile[]" type="file" />
どうやらrailsのフォームヘルパーは
name=モデル名[属性(カラム)名]
id=モデル名_属性(カラム)名
で出力して送信の際にはnameを使って処理するらしい。
ターミナルのログを見ると
Started POST "/posts" for ::1 at 2021-01-26 12:11:25 +0900
Processing by PostsController#create as HTML
Parameters: {
"authenticity_token"=>"",
"post"=>{"image"=>#<
ActionDispatch::Http::UploadedFile:0x000055659a002788
@tempfile=#<Tempfile:/tmp/RackMultipart20210126-9817-k74a92.jpg>,
@original_filename="0e47c4bb12896adb102c93237bbe9554.jpg",
@content_type="image/jpeg",
@headers="Content-Disposition: form-data;
#これ
name=\"post[image]\";
filename=\"0e47c4bb12896adb102c93237bbe9554.jpg\"\r\nContent-Type:
image/jpeg\r\n"
>,
"body"=>""}, "commit"=>"送信"}
参考:Understanding Parameter Naming Conventions
railsガイドには
どのフォームinputを使う場合でも、id属性はinputのnameから生成されます。
これらのidは、cssでのスタイル追加やJavaScriptによるフォーム制御で使うのに便利です。
とあるように生成されたidのほうは変更しても影響しないみたいです(自分でidを指定した場合そちらが優先して使われる)
参考:
CodePen Bootstrap4 custom-file mutiple
Action View フォームヘルパー