サーバーサイドでのvalidation結果をajaxのフォームに反映させたい時ありますよね。
htmlをエラーメッセージ表示部分を固定で準備しておきます。エラーが起こったら表示するために、最初はhideを指定して表示されなくします。
<div class="alert alert-danger hide" id="image-new-error-message">
データの登録に失敗しました。
<div id="errors"><ul/></div>
</div>
フォームはremoteを指定してajaxで動作するようにします。
<%= form_for Image.new, :html => {:multipart => true, :id => "image-new-form"}, :remote => true do |f| %>
<%= f.file_field :image %>
<%= f.text_field :title %>
<%= f.form_group do %>
<%= f.submit "新規登録する" %>
<% end %>
コントローラー側の処理は保存に成功したら何も返さない。エラーが発生したらjsonでメッセージをそのまま返します。
def create
@image = Image.new(params.require(:image).permit([:image, :title]))
if @image.save
render nothing: true, status: 200
else
render json: {errors: @image.errors.messages}, status: 422
end
end
エラー時にはこんな感じでjsonが帰ってきます。
{"errors":{"image":["写真を入力してください。"],"title":["タイトルは64文字以内で入力してください。"]}}
javascriptでは、エラーだったらjsonをパースして<ul/>になっている箇所にエラーメッセージを追加。エラーが起こったフィールドから、親のform-groupを特定してhas-errorを追加します。
ポストする前と、成功した時でエラーの表示を取り除きます。
$("#image-new-form").on("ajax:error", function(event, xhr) {
$("#image-new-error-message").removeClass('hide').show();
var errors = JSON.parse(xhr.responseText).errors;
$.each(errors, (function(k, v){
$("#errors ul").append("<li>" + v + "</li>");
$("#image_" + k).closest(".form-group").addClass("has-error")
}));
}).on("ajax:success", function() {
$("#image-new-error-message").hide();
$("#errors ul").empty();
$("#image-new-form" ).find(".form-group").removeClass("has-error")
}).on("ajax:before", function() {
$("#image-new-error-message").hide();
$("#errors ul").empty();
$("#image-new-form" ).find(".form-group").removeClass("has-error")
});
これでajaxのフォームにvalidationのメッセージがそのまま反映されます。
この実装ではhelp-blockに触れていません。