添加Stimulus后Rails表单失效:提交报错与验证提示异常
Rails表单结合Stimulus图片预览后功能异常问题
我在Rails表单里用Stimulus实现了图片上传预览,预览功能正常,但表单本身出了问题:
- 点击提交时出现错误:
Error: Form responses must redirect to another location,没有其他反应; - 空字段的提示弹窗不再工作。
试过用form_tag包裹整个表单,但提交时直接跳转,就算表单不完整也不会有验证提示。唯一能恢复表单功能的方法是移除<%= javascript_importmap_tags %>,但这样Stimulus的预览功能就失效了。
相关代码
_form.html.erb
<%= form_with model: @movie do |f|%> <%= f.hidden_field :user_id, value: current_user.id %> <%= f.hidden_field :rating, value: -1%> <%= render "movies/error_messages" %> <div class="field form-group" style = "color: white"> <%= f.text_field :title, placeholder: "Title", class:"form-control"%> </div> <br> <div class="field form-group" style = "color: white"> <%= f.text_area :description, placeholder: "Description", class:"form-control" %> </div> <br> <div class="field form-group" style = "color: white"> <%= f.text_field :director, placeholder: "Director", class:"form-control" %> </div> <br> <%= f.select :genre, options_for_select([["Movie Genre", ""], "Action", "Adventure", "Drama", "Romance", "Comedy", "Horror", "Suspense"]), {}, { class: "form form-group form-control" } %> <br> <div class="field form-group" style = "color: white"> <label class="form-label" for="typeNumber">Movie Duration</label> <%= f.text_field :movie_length, class:"form-control", step:"0.01", value:"01.00", type:"number", id:"typeNumber" %> </div> <br> <div data-controller = "previews"> <label class="form-label" for="typeNumber" style = "color: white">Movie Cover</label> <%= f.file_field :image, direct_upload: true, data: { previews_target: "input", action: "change->previews#preview"}, class:"form-control" %> <div class= "image-cover-preview"> <% if @movie.image.attached? && @movie.image.persisted?%> <%= image_tag @movie.image, data: { previews_target: "preview"}, class: "image-cover-preview" %> <% else %> <%= image_tag "", data: { previews_target: "preview"}, class: "image-cover-preview" %> <% end %> </div> </div> <br> <div class="actions"> <%= f.submit class:"btn btn-primary" %> </div> <% end %>
movies_controller.rb
class MoviesController < ApplicationController before_action :set_movie, only: [ :show, :edit, :update, :destroy ] before_action :authenticate_user!, except: [ :index, :show ] def index @movies = Movie.search_by_title(params[:search]) @movies.each do |m| unless m.reviews.blank? tempRating = 0 m.reviews.each do |r| tempRating += r.rating end m.rating = tempRating/m.reviews.size end end end def show @movie = Movie.find(params[:id]) @reviews = Review.where(movie_id: @movie.id).order("created_at DESC") @users = User.all unless @reviews.blank? tempRating = 0 @reviews.each do |r| tempRating += r.rating end @movie.rating = tempRating/@reviews.size end end def new @movie = Movie.new end def edit end def create @movie = Movie.new(movie_params) if movie_params[:image].blank? @movie.errors.add(:base, "Please upload an image") render :new else if @movie.save redirect_to @movie, notice: "Movie was successfully created" else render :new end end end def update if @movie.update(movie_params) redirect_to @movie, notice: "Movie was successfully updated" end end def destroy @movie.destroy redirect_to movies_path end private def set_movie @movie = Movie.find(params[:id]) end def movie_params params.require(:movie).permit(:title, :description, :director, :genre, :movie_length, :rating, :user_id, :image, :search) end end
previews_controller.js
import { Controller } from "@hotwired/stimulus" export default class extends Controller { static targets = ["input", "preview"]; connect() { } preview() { let input = this.inputTarget; let preview = this.previewTarget; let file = input.files[0]; let reader = new FileReader(); reader.onloadend = function() { preview.src = reader.result; }; if(file) { reader.readAsDataURL(file); } else { preview.src = ""; } } }
解决方案
问题根源
form_with 默认会以AJAX方式提交表单,而控制器在验证失败时使用 render :new 返回HTML,不符合AJAX请求的响应要求,才会抛出Error: Form responses must redirect to another location错误。同时,AJAX提交会绕过原生HTML5表单验证,导致空字段提示失效。
修复步骤
关闭表单的AJAX提交
在form_with里添加local: true选项,强制表单以传统方式提交:<%= form_with model: @movie, local: true do |f|%>这样表单提交会回到正常的页面刷新模式,验证失败时
render :new能正确返回表单页面,空字段的HTML5提示也会生效。修复update方法的异常分支
update方法只处理了更新成功的情况,失败时无响应会导致服务器返回204无内容,引发异常。补充失败分支:def update if @movie.update(movie_params) redirect_to @movie, notice: "Movie was successfully updated" else render :edit # 失败时返回编辑页面,显示错误信息 end end优化图片预览的标签ID冲突
表单里电影时长标签的for="typeNumber"和图片封面标签重复,修改图片封面的标签关联:<label class="form-label" for="movie_image" style = "color: white">Movie Cover</label> <%= f.file_field :image, direct_upload: true, data: { previews_target: "input", action: "change->previews#preview"}, class:"form-control", id: "movie_image" %>避免标签关联错误。
做完这些修改后,表单的提交验证、错误提示都会恢复正常,同时Stimulus的图片预览功能也能继续工作。
内容的提问来源于stack exchange,提问作者JMS
相关产品推荐
相关产品推荐

