-1

My goal is to when adding a new product with the new product form, to have an input where one can add a list of emails separated by a space. The list of emails in this string field would be saved as an array of emails in the email_list array attribute of the Product model. This way each product has many emails. (later an email will be sent to these users to fill out questionaire, once a user fills it out there name will be taken off this list and put on completed_email_list array.

I am relatively new to rails, and have a few questions regarding implementing this. I am using postgresql, which from my understanding I do not need to serialize the model for array format because of this. Below is what I have tried so far to implement this. These may show fundamental flaws in my thinking of how everything works.

My first thinking was that I can in my controllers create action first take params[:email].split and save that directly into the email_list attribute (@product.email_list = params[:email].split. It turns out that params[:email] is always nil. Why is this? (this is a basic misunderstanding I have)(I put :email as accepted param).

After spending a long time trying to figure this out, I tried the following which it seems works, but I feel this is probably not the best way to do it (in the code below), which involves creating ANOTHER attribute of string called email, and then splitting it and saving it in the email_list array :

   @product.email_list = @product.email.split

What is the best way to actually implement this? someone can clear my thinking on this I would be very grateful.

Cheers

Products.new View

<%= simple_form_for @product do |f| %>
  <%= f.input :title, label:"Product title"  %>
  <%= f.input :description  %>
  <%= f.input :email %>
  <%= f.button :submit %>
<%end %>

Products Controller

class ProductsController < ApplicationController
  before_action :find_product, only: [:show, :edit, :update, :destroy]

  def index
    if params[:category].blank?
      @products= Product.all.order("created_at DESC")
    else
      @category_id=Category.find_by(name: params[:category]).id
      @products= Product.where(:category_id => @category_id).order("created_at DESC")
    end
  end

  def new
    @product=current_user.products.build
    @categories= Category.all.map{|c| [c.name, c.id]}
  end

  def show
  end

  def edit
    @categories= Category.all.map{|c| [c.name, c.id]}
  end

  def update
    @product.category_id = params[:category_id]

    if @product.update(product_params)
      redirect_to product_path(@product)
    else
      render 'new'
    end
  end

  def destroy
    @product.destroy
    redirect_to root_path
  end

  def create
    @product=current_user.products.build(product_params)
    @product.category_id = params[:category_id]
    @product.email_list = @product.email.split
    if @product.save
      redirect_to root_path
    else
      render 'new'
    end
  end

  private

  def product_params
    params.require(:product).permit(:title, :description, :category_id, :video, :thumbnail,:email, :email_list)
  end

  def find_product
    @product = Product.find(params[:id])
  end
end
adesurirey
  • 2,549
  • 2
  • 16
  • 36
justlivinglife
  • 29
  • 1
  • 1
  • 6

3 Answers3

0

I'd say that what you have is perfectly fine, except for the additional dance that you're doing in @product.email_list=@product.email.split, which seems weird.

Instead, I'd have an emails param in the form and an #emails= method in the model (rather than email and #email=):

def emails=(val)
  self.email_list = val.split
end

Alternatively, you could do that in the controller rather than having the above convenience #emails= method, similar to the way you're handling the category_id:

@product = current_user.products.build(product_params)
@product.category_id = params[:category_id]
@product.email_list = product_params[:emails].split
fphilipe
  • 9,739
  • 1
  • 40
  • 52
0

Because you need validations on your emails and to make it cleaner I would create an email table, make Product table accept Email attribues and use cocoon gem to have a nice dynamic nested form with multiple emails inputs.

1) models

class Product < ActiveRecord::Base
  has_many :emails, dependent: :destroy
  accepts_nested_attributes_for :emails, reject_if: :all_blank, allow_destroy: true
end

class Email < ActiveRecord::Base
  belong_to :product
  validates :address, presence: true
end

2) Controller

class ProductsController < ApplicationController
  def new
    @product = current_user.products.build
  end

  def create
    @product = current_user.products.build(product_params)
    if @product.save
      redirect_to root_path
    else
      render 'new'
    end
  end

  private

  def product_params
    params.require(:project).permit(:title, :description, :category_id, :video, :thumbnail, emails_attributes: [:id, :address, :_destroy])
  end
end

3) View

<%= simple_form_for @product do |f| %>
  <%= f.input :title, label:"Product title"  %>
  <%= f.input :description  %>
  <%= f.association :category %>

  <div id="emails">
    <%= f.simple_fields_for :emails do |email| %>
      <%= render 'emails_fields', f: email %>
      <div class="links">
        <%= link_to_add_association 'add email', f, :emails %>
      </div>
    <%= end %>
  </div>

  <%= f.button :submit %>
<% end %>

In your _emails_fields partial:

<div class="nested-fields">
  <%= f.input :address %>
  <%= link_to_remove_association "Remove email", f %>
</div>

Then setup cocoon's gem and javascript and you'll be good.

Reference: https://github.com/nathanvda/cocoon

adesurirey
  • 2,549
  • 2
  • 16
  • 36
  • Thanks mate, in my case the emails will likely be copy and pasted from a data base, each product will have hundreds of emails, so I was thinking it was best not to have the multiple inputs for each single email like the cocoon gem? Is that right? – justlivinglife Jul 13 '19 at 11:58
  • @justlivinglife Sorry the answer doesn't help, but look how I handled product categories in the form with a `f.association` so you don't have to handle it in controller. That's my bonus. – adesurirey Jul 13 '19 at 12:11
0

To solve your original issue

@product.email_list = params[:email].split. It turns out that params[:email] is always nil

:email is a sub key of :product hash, so it should be:

@product.email_list = params[:product][:email].split

Demo:

params = ActionController::Parameters.new(product: { email: "first@email.com last@email.com" })
params[:email] # => nil
params[:product][:email] # => "first@email.com last@email.com"
adesurirey
  • 2,549
  • 2
  • 16
  • 36