Ruby on Rails:当包含用于在DB中创建新对象的表单时,为什么显示空值?

时间:2019-02-05 16:43:23

标签: ruby-on-rails

我是新手。我正在尝试开发一个涉及商店和糖果的简单Web应用程序,其中商店可以有很多糖果。

我的shop / show.html.erb中有以下代码,该代码两次显示糖果列表。

<% i=0 %>
<% for candy in @shop.candies do %>
    <% i+=1 %>
    <%= i %> <%= candy.name  %>
<% end %>

<%= form_for([@shop, @shop.candies.build]) do |f| %>
            <%= render(:partial => 'form',
                       :locals => {:f => f, :header => "Add a Candy", 
                       :placeholder => "Enter a candy name"}) %>
<% end %>

<% i=0 %>
<% for candy in @shop.candies do %>
    <% i+=1 %>
    <%= i %> <%= candy.name  %>
<% end %>

我在_form.html.erb中创建新糖果的代码:

<%= f.text_field(:name, :placeholder=> placeholder, :class=>"form-control custom-input")%>
<%= button_tag( :class => "btn btn-primary mb-2 btn-custom btn-custom-sc") do %>
      <i class="fas fa-plus icon"></i>
<% end %>

商店代码控制者:

class ShopsController < ApplicationController

def show
       @shop = Shop.find(params[:id])
       @unshelved_candies = @shop.candies.unshelved_candies
    end
    private
        def shop_params
          params.require(:shop).permit(:name)
        end

end

糖果控制器代码:

class CandiesController < ApplicationController

   def create
      @shop = Shop.find(params[:shop_id])
      @candy = @shop.candies.create(candy_params)
      redirect_to(shop_path(@shop))
    end

    private

            def candy_params
              params.require(:candy).permit(:name)
            end   
   end

end

当我运行代码并在浏览器上查看时,我注意到它在第二个循环中创建了一个空糖(不在数据库中)。但是,当我删除用于创建糖果的表单时,它的行为应达到预期。我无法理解为什么它又要循环一次并显示空白值。 第一个循环的输出是正确的:

  1. 糖果1
  2. 糖果2
  3. 糖果3

第二个循环的输出是:

  1. 糖果1
  2. 糖果2
  3. 糖果3
  4. [<---空。我不会在数据库中插入任何新内容]

有人可以告诉我为什么它在第二个循环中显示空白值以及如何防止这种额外的迭代吗?

1 个答案:

答案 0 :(得分:3)

我相信“额外”糖果就是您要在此处实例化的糖果:

<%= form_for([@shop, @shop.candies.build]) do |f| %>

新糖果的糖果名称为nil,因此您将获得空白。

顺便说一句,这个:

<% i=0 %>
<% for candy in @shop.candies do %>
  <% i+=1 %>
  <%= i %> <%= candy.name  %>
<% end %>

让我惊叹为非惯用的红宝石。我希望看到更多类似的东西:

<% @shop.candies.each.with_index(1) do |candy, index| %>
  <%= index %> <%= candy.name  %>
<% end %>

我想确保您不会得到多余糖果的蛮力方式是做类似的事情:

<% @shop.candies.each.with_index(1) do |candy, index| %>
  <% unless candy.new_record? %>
    <%= index %> <%= candy.name  %>
  <% end %>
<% end %>

您也可以尝试:

<%= form_for([@shop, @candy]) do |f| %>

我相信可以写成这样:

<%= form_for(@shop, @candy) do |f| %>    

如果您想节省几个按键(它们随着时间的推移会累加)。

然后在您的ShopsController中执行:

class ShopsController < ApplicationController

  def show
    @shop = Shop.find(params[:id])
    @candy = Candy.new
    @unshelved_candies = @shop.candies.unshelved_candies
  end

  private

  def shop_params
    params.require(:shop).permit(:name)
  end

end

这也很好,因为它避免了:

@shop.candies.build

这要求您的viewshopcandies之间的关系有很多了解,还要求您的view直接与数据库进行交互。

由于您显然正在使用嵌套路由,因此您可能需要查看shallow: true指令。

(这与您的问题无关),您可能想对Law of Demeter有所考虑。我注意到您这样做:

@unshelved_candies = @shop.candies.unshelved_candies

我个人想做些类似的事情:

@unshelved_candies = @shop.unshelved_candies

Shop中,您可能会有类似的内容:

class Shop < ApplicationRecord

  def unselved_candies
    candies.unshelved
  end

end

Candy中,类似:

class Candy < ApplicationRecord

  class < self 

      def unshelved
        where(shelved: false) # or however you determine a candy is unshelved
      end

  end

end

许多人会将unshelved设为scope,这是做同一件事的另一种方式。

这样,您的ShopsController对商店和糖果之间的关系和搁置状态的机制了解得更少。 FWIW。