对于PostgreSQL数组,值在Rails表单中重复

时间:2017-05-26 00:54:20

标签: ruby-on-rails forms postgresql

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

E.g。在下面的表格中,我有2个输入字段,因此您可以在数组列中为Pet存储2个昵称。我把" a"在字段1和" b"在字段2中按保存。我在Rails控制台中检查并正确存储:nicknames: ["a", "b"]。但是当我打开表单时,两个输入字段都包含" a b"。所以价值观以某种方式连接起来。我做错了什么?

重现:

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

在控制器中:

def pet_params
  params[:pet].permit(nicknames: [])
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 %>

创建一个带有昵称的新宠物&#34; a&#34;和&#34; b&#34;。去编辑宠物,输入现在包含&#34; a b&#34;和&#34; b&#34;,但它存储为&#39;昵称:[&#34; a&#34;,&#34; b&#34;]&#39;根据Rails控制台在数据库中。

4 个答案:

答案 0 :(得分:2)

检查是否@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>

Here is the documentation for persisted?

答案 1 :(得分:1)

简单表单不知道如何执行此操作,因此您可以编写自己的自定义输入(在gem README和上面的@ Thanh&#39链接中描述),或者您可以告诉它要使用的值:

<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>

答案 2 :(得分:1)

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

此处的一个选项是保持表单简单,但使用Selectize.js等JavaScript库来添加功能(直接集成,或通过selectize-rails)。这允许用户使用昵称作为列表,并节省您编写额外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>
  ...

你可以像这样初始化输入:

$('.pet_nicknames').selectize({
  delimiter: ',',
  placeholder: 'Type in nickname(s)',
  create: function(input) {
    return {
      value: input,
      text: input
    }
  }
});

您可以查看usage options for Selectize here,例如如果您需要限制用户可以输入的最大昵称数。

答案 3 :(得分:1)

每个字段都有a b,因为您有相同的字段::nicknames

你应该做的是为每一个生成多个字段,几乎就像保罗在这里说的那样,但方法略有不同。

在您看来:

<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>

即使在new记录中,也会为昵称呈现一个字段,其中还没有昵称。如果你edit,每个昵称都会有一个字段,而一个昵称则为空。

还有一个隐藏的禁用字段,用于呈现给您的下一个任务。

要使其完全正常运行,您应该添加javascript功能,以添加或销毁昵称字段。

在您的javascript资源中:

$('#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();
});

因此,第一个处理程序为昵称添加新字段,第二个处理程序删除元素。注意:这不是copypaste准备好的,特别是去除。您可能希望处理应删除的元素(可能包含一些lidiv包含字段和删除按钮本身。)

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