使用 Rails 7 turbo 流作为完全替代品的上下文测试 rails-ujs 功能。
类嵌套结构如下:
class Cartitem < ApplicationRecord
belongs_to :article
belongs_to :cart
validates :quantity_ordered, numericality: { greater_than: 0 }, on: :create
validates :quantity_ordered, numericality: { greater_than_or_equal_to: 0 }, on: :update
end
一页文章可能会创建caritems,每篇文章一个。
每篇文章都需要可创建、可更新、可删除(因此可以为不断改变主意的用户重新创建)。
因此,该页面对每篇文章都有一个块
<% articles.each do |article| %>
<%= render 'article', article: article %>
<% end %>
# article partial
<div>
<%= article.description_short %>
</div>
<div>
<%= turbo_frame_tag dom_id(article) do %>
<%= render 'cartitem_block', article: article %>
<% end %>
</div>
部分然后调用涡轮帧:
<%= turbo_frame_tag dom_id(article) do %>
<%= form_with(model: @cartitem, local: false) do |f| %>
<%= f.number_field :quantity_ordered %>
<%= f.submit %>
<% end %>
<% end %>
呈现的 HTML 确实调用了 dom_ids 并按预期呈现:
<turbo-frame id="article_1">
<form action="/cartitems" accept-charset="UTF-8" data-remote="true" method="post"><input type="hidden" name="authenticity_token" value="sgABxVmZX0TDditdtwuGrgG4t9fkdfFMg02tpDnfgX3ch_IqD_YGoFJroE4u3y9t-bdLfGAyXZBUyJe04RBqtQ" autocomplete="off">
<input min="1" type="number" name="cartitem[quantity_ordered]" id="cartitem_quantity_ordered">
<button name="button" type="submit" ></button>
</form>
</turbo-frame>
[...]
<turbo-frame id="article_5">
<form action="/cartitems" accept-charset="UTF-8" data-remote="true" method="post"><input type="hidden" name="authenticity_token" value="..." autocomplete="off">
<input min="1" type="number" name="cartitem[quantity_ordered]" id="cartitem_quantity_ordered">
<button name="button" type="submit" ></button>
</form>
</turbo-frame>
控制器:
def create
@article = Article.find(params[:cartitem][:article_id])
price_ordered = @article.sell_price * params[:cartitem][:quantity_ordered].to_d
@cartitem = Cartitem.create!(article_id: @article.id, quantity_ordered: params[:cartitem][:quantity_ordered].to_d, price_ordered: price_ordered, cart_id: params[:cartitem][:cart_id].to_i)
respond_to do |format|
if @cartitem.save
format.turbo_stream
format.html { redirect_to cartitem_url(@cartitem), notice: "Cartitem was successfully created." }
else
format.turbo_stream
format.html { render :new, status: :unprocessable_entity }
end
end
end
如果@cartitem 有效,则进程运行并按预期呈现:
Rendered cartitems/_cartitem.html.erb (Duration: 1.0ms | Allocations: 953)
Rendered cartitems/create.turbo_stream.erb (Duration: 1.3ms | Allocations: 1082)
但是,在使用空值提交时,为了测试验证,会出现意外结果:
Started POST "/cartitems" for 127.0.0.1 at 2022-01-22 08:24:38 +0100
Processing by CartitemsController#create as TURBO_STREAM
Parameters: {"authenticity_token"=>"[FILTERED]", "cartitem"=>{"price_ordered"=>"3.41", "cart_id"=>"1", "article_id"=>"1", "fulfilled"=>"false", "quantity_ordered"=>""}}
[...]
TRANSACTION (0.2ms) ROLLBACK
TRANSACTION (0.2ms) ROLLBACK
↳ app/controllers/cartitems_controller.rb:28:in `create'
Completed 422 Unprocessable Entity in 11ms (ActiveRecord: 1.1ms | Allocations: 4874)
ActiveRecord::RecordInvalid (Validation failed: Quantity ordered must be greater than 0):
正如预期的那样,事务回滚。但是,响应是不可处理的实体。
并且返回不是create.turbo_stream.erb
声明要做的,即如果 caritem 为 nil 则重新渲染部分。
*注意:else
respond_to 块的部分在引用和不引用 turbo_stream 的情况下都进行了尝试,结果相同。
<% if @cartitem %>
<%= turbo_stream.replace "article_#{article.id}", @cartitem %>
<% else %>
<%= render 'cartitem_block', article: @article %>
<% end %>
# _cartitem partial:
<%= turbo_frame_tag dom_id(@cartitem.article), class: 'fade-text' do %>
<%= form_with(url: update_q_cartitem_path(@cartitem), local: false, method: :patch) do |form| %>
<%= form.number_field :quantity_ordered, value: number_with_precision(@cartitem.quantity_ordered, precision: 0), min: 0, style: 'width: 69px; display: inline-block;' %>
<%= button_tag(style: 'display: inline-block;') do %>
<p class='button warning' style='margin-top: 10px;'><%= fa_icon('recycle', class: 'fa 3x') %></p>
<% end %>
<% end %>
<% end %>
相反,返回的xhr
响应是在开发模式下返回的错误的 HTML 页面。
浏览器控制台还抱怨
Response has no matching <turbo-frame id="article_1"> element
这是不真实的,因为 HTML 仍然挥之不去:
<turbo-frame id="article_1"></turbo-frame>
需要做些什么来正确处理错误,将表单部分呈现在正确识别的位置?