Ruby on rails 值在PostgreSQL数组的Rails表单中重复

Ruby on rails 值在PostgreSQL数组的Rails表单中重复,ruby-on-rails,forms,postgresql,Ruby On Rails,Forms,Postgresql,如果我将数组保存到数据库中,它会被正确地持久化,但是当我查看表单时,它会在所有数组输入字段中的数组中放置连接的值 例如,在下表中,我有2个输入字段,因此您可以在数组列中存储宠物的2个昵称。我在字段1中输入“a”,在字段2中输入“b”,然后按save。我在Rails控制台中检查并正确地存储了它:昵称:[“a”,“b”]。但是当我打开表单时,两个输入字段都包含“ab”。因此,这些值以某种方式连接在一起。我做错了什么 复制: rails g scaffold Pets 然后在新的迁移中: class

如果我将数组保存到数据库中,它会被正确地持久化,但是当我查看表单时,它会在所有数组输入字段中的数组中放置连接的值

例如,在下表中,我有2个输入字段,因此您可以在数组列中存储宠物的2个昵称。我在字段1中输入“a”,在字段2中输入“b”,然后按save。我在Rails控制台中检查并正确地存储了它:
昵称:[“a”,“b”]
。但是当我打开表单时,两个输入字段都包含“ab”。因此,这些值以某种方式连接在一起。我做错了什么

复制:

rails g scaffold Pets
然后在新的迁移中:

class CreatePets < ActiveRecord::Migration
  def change
    create_table :pets do |t|
      t.string :nicknames, array: true
      t.timestamps null: false
    end
  end
end
形式如下:

<%= simple_form_for(@pet) do |f| %>
  <%= f.error_notification %>

  <div class="form-inputs">
    <%= f.text_field :nicknames, name: 'pet[nicknames][]' %>
    <%= f.text_field :nicknames, name: 'pet[nicknames][]' %>
  </div>

  <div class="form-actions">
    <%= f.button :submit %>
  </div>
<% end %>


创建一个昵称为“a”和“b”的新宠物。转到编辑Pet,输入现在包含“ab”和“ab”,但根据Rails控制台,它在数据库中存储为“昵称:[“a”,“b”]”。

Simple Form不知道如何操作,因此您可以编写自己的自定义输入(在gem自述和上面@Thanh的链接中描述),或者您可以告诉它要使用哪些值:

<div class="form-inputs">
  <%= f.text_field :nicknames, name: 'pet[nicknames][]', value: @pet.nicknames.try(:first) %>
  <%= f.text_field :nicknames, name: 'pet[nicknames][]', value: @pet.nicknames.try(:second) %>
</div>

我假设昵称是由用户输入的,而不是从列表中选择的。因此,您是否考虑过如何添加和删除昵称?只有两个并不太麻烦,但将它们存储在
数组中意味着在某个时候可能会有更多

这里的一个选项是保持表单简单,但使用JavaScript库(例如)添加功能(直接集成或通过)。这允许用户将昵称作为列表使用,并节省您编写附加JS以添加或删除条目的工作量

然后,您的表单简化为:

<%= simple_form_for(@pet) do |f| %>
  <%= f.error_notification %>

  <div class="form-inputs">
    <%= f.input :nicknames %>
    <%# untested - you might need to append `input_html: { value: @pet.nicknames.join(',') }` %>
  </div>
  ...

例如,如果需要限制用户可以输入的昵称的最大数量,您可以检查。每个字段中都有相同的
ab
,因为您有相同的字段:
:昵称

您应该做的是为每个字段生成多个字段,就像Paul在这里所说的那样,但方法略有不同

在你看来:

<div class="form-inputs">
  <% @pet.nicknames.each do |nickname| %> 
    <%= f.text_field :nicknames, name: 'pet[nicknames][]', value: nickname %>
  <% end %>
  <%= f.text_field :nicknames, name: 'pet[nicknames][]' %>
  <%= f.hidden_field :nicknames, name: 'pet[nicknames][]', disabled: true, class: 'js-nickname-input-template' %>
</div>
因此,第一个处理程序为昵称添加新字段,第二个处理程序删除元素。注意:这不是复制粘贴准备,尤其是删除。您可能需要处理应该删除哪个元素(可能是一些
li
div
包含字段并删除按钮本身)

tl;dr:这个想法是为每个现有的数组元素服务器端创建字段,再加上一个空字段,再加上一个模板。
您可以使用模板使用javascript添加和删除字段。

检查
@pet.persisted

如果是,则循环数值并显示。

<div class="form-inputs">
  <% if @pet.persisted? && (@pet.nicknames.length > 0) %>   
    <% @pet.nicknames.each do |nickname| %> 
        <%= f.text_field :nicknames, name: 'pet[nicknames][]', value: nickname %>
    <% end %>
  <% else %>
    <%= f.text_field :nicknames, name: 'pet[nicknames][]' %>
    <%= f.text_field :nicknames, name: 'pet[nicknames][]' %>
  <% end %>
</div>
如果没有,请像往常一样处理新问题。

<div class="form-inputs">
  <% if @pet.persisted? && (@pet.nicknames.length > 0) %>   
    <% @pet.nicknames.each do |nickname| %> 
        <%= f.text_field :nicknames, name: 'pet[nicknames][]', value: nickname %>
    <% end %>
  <% else %>
    <%= f.text_field :nicknames, name: 'pet[nicknames][]' %>
    <%= f.text_field :nicknames, name: 'pet[nicknames][]' %>
  <% end %>
</div>

0) %>   

你需要为帮助你的人提供更多的背景。什么是表单对象?它是如何在控制器中初始化的?您的控制器如何处理验证错误?@AdamLassek很好,我已经更新了描述,使其更通用,并具有完整的复制步骤@我认为这不是一个正确的方法,因为宠物可以有很多昵称,所以你不知道你需要添加多少字段。您可以创建另一个名为
昵称
的模型,并设置一个关系,以便宠物拥有许多昵称,昵称属于宠物,然后使用嵌套形式。如果你还想这样做,也许这篇文章可以帮助你:@Thanh谢谢你的输入!添加数量可变的输入框不是问题。另外,这是遗留代码,除了这个小问题应该可以解决外,它工作得很好。@PeterEvjan,所以我认为您需要在编辑页面上手动显示昵称,检查
@pets
是否保持,然后迭代
@pets.昵称
,并插入带有昵称值的单独文本字段。这种方法非常必要。如果用户想要添加更多宠物怎么办?如果只有一个宠物和用户加载
edit
?(只有一个字段)您要做的是区分没有宠物和至少一只宠物的逻辑。您的
@pet.nicknames.length>0
是多余的,因为如果
@pet.nicknames.length==0
,则无论如何都不会进行迭代
@pet.persistend?
在验证失败时会中断逻辑:它们不会被持久化,
edit
不会呈现您刚才输入的内容。因此,这种方法是不正确的。看看我的方法,它适用于任何可能的场景,即使没有javascript。如果验证失败,打破逻辑是正确的方法,并且已经存在的值将以任何方式显示I misspoke,add more昵称,而不是pets。在第二句中,“只有一个昵称和用户加载
edit
”。如果验证失败,您的方法将显示两个字段,每个字段都有OP已经遇到的相同问题。因为,昵称将不会被持久化,而您将退回到OP已经使用的方法。
$('#some-button-for-new').click(function(e){
  $templ = $('.js-nickname-input-template');
  $el = $templ.clone();
  $el.removeClass('.js-nickname-input-template');
  $el.removeAttr('disabled');
  $el.attr('type', 'text');
  $templ.before($el);
});

$('.some-button-for-destroy').click(function(e){
  $el = $($(e.target).data('identifier'));
  $el.remove();
});
<div class="form-inputs">
  <% if @pet.persisted? && (@pet.nicknames.length > 0) %>   
    <% @pet.nicknames.each do |nickname| %> 
        <%= f.text_field :nicknames, name: 'pet[nicknames][]', value: nickname %>
    <% end %>
  <% else %>
    <%= f.text_field :nicknames, name: 'pet[nicknames][]' %>
    <%= f.text_field :nicknames, name: 'pet[nicknames][]' %>
  <% end %>
</div>