2

使用 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 则重新渲染部分。
*注意:elserespond_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>

需要做些什么来正确处理错误,将表单部分呈现在正确识别的位置?

4

1 回答 1

0

提交的参数没有创建有效对象的条件应该是明确的,特别是当情况取决于父对象时:

else
  format.turbo_stream { render turbo_stream:  turbo_stream.replace( @article, partial: 'carts/cartitem_block', locals: { article: @article } ) }
end

否则 turbo_stream 坚持尝试渲染 create turbo_stream 变体,在一种情况下调用 NULL 对象@cartitem并因此失败并且无法继续。

于 2022-01-25T08:31:34.653 回答