I have this form:
<% form_tag add_expt_details_samples_path, :method => :post do %>
<% for sample in #samples %>
<% if sample.study_id == #study.id %>
<% fields_for "samples[]", sample do |form| %>
<fieldset>
<legend>Sample Name: <%= sample.name %></legend>
<p><center><%= form.label :sample_title %>
<%= form.text_field :sample_title, :size => 25 %></center></p>
<table>
<tr>
<td>
<%= form.label :taxon_id %>
<%= form.text_field :taxon_id, :size => 15 %>
</td>
<td>
<%= form.label :scientific_name %>
<%= form.text_field :scientific_name, :size => 20 %>
</td>
<td>
<%= form.label :common_name %>
<%= form.text_field :common_name, :size => 15 %>
</td>
</tr>
</table>
<p>
<center>
<%= form.label :sample_descripition %><br \>
<%= form.text_area :description %>
</center>
</p>
</fieldset>
<% end %>
<% end %>
<% end %>
<p><center><%= submit_tag "Next" %></center></p>
<% end %>
I would like to collect the samples[] in add_expt_details action so that I could render a post method view for adding new model records for the samples[] ids.
my action is as follows
def add_expt_details
# Collect the samples that were sent
#expt_samples = Sample.find(params[:samples])
# #expt_samples.each do |samp|
# samp.expts.build
# end
end
and then send them to a create action where the sample details are updated and the expt values can be created.
def create_study
#samples = Sample.update(params[:samples].keys, params[:samples].values).reject { |p| p.errors.empty? }
if #samples.empty?
flash[:notice] = "Samples updated"
redirect_to :action => "add_expt_details", :anchor => "ok"
else
render :action => 'edit_individual'
end
end
is there a way to achieve this, I have used accepted_nested_attributes_for :expts, :dependent => :destroy. But I seem to be wrong some where and I do not want to have a form with many fields.
The error message I get is "Unknown key(s): 3, 6, 12"
All suggestions are appreciated.
Related
Hey guys im developing a rails application that stores quotes in a database and then allows you to search through the quotes using a simple search. I have implemented the search form but the results do not appear and I cant figure out why.
controller:
class BasicsController < ApplicationController
def quotations
#quotations = Quotation.all
if params[:search]
#quotations = Quotation.search(params[:search]).order("created_at DESC")
else
#quotations = Quotation.all.order("created_at DESC")
end
if params[:quotation]
#quotation = Quotation.new( params[:quotation] )
if #quotation.save
flash[:notice] = 'Quotation was successfully created.'
#quotation = Quotation.new
end
elsif
#quotation = Quotation.new
end
if params[:sort_by] == "date"
#quotations = Quotation.order(:created_at)
else
#quotations = Quotation.order(:category)
end
end
end
model:
class Quotation < ApplicationRecord
def self.search(search)
where("author_name LIKE ? OR quote LIKE ?", "%#{search}", "%#{search}")
end
end
view:
<%= form_tag basics_quotations_path, :method => 'get' do %>
<p>
<%= text_field_tag :search, params[:search], placeholder: "Search Quotations" %>
<%= submit_tag "Search", :name => nil %>
</p>
<% end %>
<h3>Quotations</h3>
<ul>
<% for quotation in #quotations %>
<li><%= h quotation.author_name %>: <%= h quotation.quote %></li>
<% end %>
</ul>
<br/>
<% if params[:sort_by] == "date" %>
<%= link_to "Sort by category", :action => :quotations, :sort_by => :category %>
<% else %>
<%= link_to "Sort by date", :action => :quotations, :sort_by => :date %>
<% end %>
<hr/>
<h3>New quotation</h3>
<%= form_for #quotation, :url => { :action => :quotations } do |form| %>
<fieldset>
<legend>Enter details</legend>
<div class="form_row">
<%= form.label :author_name %>
<%= form.text_field :author_name, :size => 20, :maxlength => 40 %>
</div>
<div class="form_row">
<%= form.label :category %>
<% #cats = [] %>
<% Quotation.select('DISTINCT category').map(&:category).each do |element| %>
<% #cats << element %>
<% end %>
<%= form.select(:category,options_for_select([[#cats[0],1],[#cats[1], 2], [#cats[2],3]])) %>
</div>
<div class="form_row">
<%= form.label :new_category%>
<%= form.text_field :category , :size =>20 , :maxlength => 40 %>
</div>
<div class="form_row">
<%= form.label :quote %>
<%= form.text_area :quote, :rows => 2, :cols => 40, :maxlength => 500 %>
</div>
</fieldset>
<p>
<div class="form_row">
<%= form.submit 'Create' %>
</div>
</p>
<% end %>
routes:
Rails.application.routes.draw do
get 'basics/quotations'
resources :quotation, :quotations
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
You've got EVERYTHING in one action, which isn't great. You might want to review some of the simple rails tutorials available.
On the subject of search in particular, note the last four lines of your method...
if params[:sort_by] == "date"
#quotations = Quotation.order(:created_at)
else
#quotations = Quotation.order(:category)
end
So regardless of the results of your seach, you'll replace #quotations at that point with all quotations in either created_at or category order.
It is not wise to place all the stuffs into a single action,your action should be very clear and well defined.But you can achieve what you wanted to do like this.
class BasicsController < ApplicationController
before_action :new_quotation, only:[:search_quotations,:index,:quotations]
def search_quotations
respond_to do |format|
if params[:search]
#quotations = Quotation.search(params[:search]).order("created_at DESC")
else
#quotations = Quotation.all.order("created_at DESC")
end
if params[:sort_by] == "date" && quotations.present?
#quotations = #quotations.order(:created_at)
else
#quotations = #quotations.order(:category)
end
format.js{}
end
end
def quotations
if params[:quotation]
#quotation = Quotation.new( quotation_params)
if #quotation.save
flash[:notice] = 'Quotation was successfully created.'
end
redirect_to root_path
end
end
def index
#quotations = Quotation.all
end
private:
def new_quotation
#quotation = Quotation.new
end
//If you are using rails4 for later version then go for this line.
def quotation_params
params.require(:quotation).permit(:author_name, :quote,:category)
end
end
You need to actually separate the logic into above actions and where 'quotation' action is meant for creating a new quotation and 'seach_quotation' is for searching all the quotations and it should returns js response cause we are going to need this while rendering a partial '_list.html.erb'.
Your view(index.htm.erb) will be looking like this.
<div>
<%= form_tag basics_search_quotations_path, :method => 'get', remote: true do %>
<p>
<%= text_field_tag :search, params[:search], placeholder: "Search Quotations" %>
<%= submit_tag "Search", :name => nil %>
</p>
<% end %>
</div>
#This partial will be used for refreshing the quotations list via remote true feature for searching and sorting.
<div id="quotation_list">
<%= render 'basics/shared/list',{quotations: #quotations} %>
</div>
br/>
<% if params[:sort_by] == "date" %>
<%= link_to "Sort by category", :action => :search_quotations, :sort_by => :category, :remote => true %>
<% else %>
<%= link_to "Sort by date", :action => :search_quotations, :sort_by => :date, :remote => true %>
<% end %>
<hr/>
<h3>New quotation</h3>
<%= form_for #quotation, :url => { :action => "quotations", :controller => "basics" } do |form| %>
<fieldset>
<legend>Enter details</legend>
<div class="form_row">
<%= form.label :author_name %>
<%= form.text_field :author_name, :size => 20, :maxlength => 40 %>
</div>
<div class="form_row">
<%= form.label :category %>
<%= form.select(:category,options_for_select(['Love','Romance','Sadness'])) %>
</div>
<div class="form_row">
<%= form.label :category%>
<%= form.text_field :category , :size =>20 , :maxlength => 40 %>
</div>
<div class="form_row">
<%= form.label :quote %>
<%= form.text_area :quote, :rows => 2, :cols => 40, :maxlength => 500 %>
</div>
</fieldset>
<p>
<div class="form_row">
<%= form.submit 'Create' %>
</div>
</p>
<% end %>
Here is the partial that display the list of Quotations /basics/shared/_list.html.erb
<h3>Quotations</h3>
<ul>
<% for quotation in #quotations %>
<li><%= h quotation.author_name %>: <%= h quotation.quote %></li>
<% end %>
</ul>
Here is the routes you need to add routes.rb
resources :quotation, :quotations
get "basics/search_quotations" => "basics#search_quotations"
post "basics/quotations" => "basics#quotations"
root 'basics#index'
Instead of performing any calculation in views its better to perform it in controller/model if needed.
so instead of this following line in your view
<% #cats = [] %>
<% Quotation.select('DISTINCT category').map(&:category).each do |element| %>
<% #cats << element %>
<% end %>
<%= form.select(:category,options_for_select([[#cats[0],1],[#cats[1], 2], [#cats[2],3]])) %>
You can create an instance variable let's say #categories or something and use it like this
<%= form.select(:category,options_for_select(#categories)) %>
And last but not the least we have to have a search_quotations.js.erb because we are sending ajax request for fetching the search result and returning 'js' response.
$("#quotation_list").html("<%= escape_javascript(render('basics/shared/list', {quotations: #quotations })) %>")
Controller: projects_controller.rb
def new
#project = Project.new
#project.repositories.build
end
def edit
#project = Project.find(params[:id])
end
Model: project_sub_type.rb
class ProjectSubType < ActiveRecord::Base
has_many :repositories, :dependent => :destroy
accepts_nested_attributes_for :repositories
end
View: _form.html.erb
<%= form_for #project, :html => {:class => 'project'} do |f| %>
<%= f.label :name, "Project name" %>
<%= f.text_field :name %>
<%= f.fields_for :repositories do |ff| %>
<%= ff.check_box :repos_name, {} , "svn_software", nil %> Svn Software
<% end %>
<%= f.fields_for :repositories do |ff| %>
<%= ff.check_box :repos_name, {} , "git_software", nil %> Git Software
<% end %>
<%= f.submit "Save"%>
edit.html.erb
<h2>Edit project</h2>
<%= render 'form' %>
Question: During create it creates the checkbox like this
<input type="checkbox" value="svn_software" name="project[repositories_attributes][0][repos_name]" id="project_repositories_attributes_0_repos_name">
<input type="checkbox" value="git_software" name="project[repositories_attributes][1][repos_name]" id="project_repositories_attributes_1_repos_name">
And it works perfectly for me. But during edit it creates 2 fields extra:
<input type="checkbox" value="svn_software" name="project[repositories_attributes][0][repos_name]" id="project_repositories_attributes_0_repos_name">
<input type="checkbox" value="svn_software" name="project[repositories_attributes][1][repos_name]" id="project_repositories_attributes_1_repos_name">
<input type="checkbox" value="git_software" name="project[repositories_attributes][2][repos_name]" id="project_repositories_attributes_2_repos_name">
<input type="checkbox" value="git_software" name="project[repositories_attributes][3][repos_name]" id="project_repositories_attributes_3_repos_name">
And also creates a hidden field as below:
<input type="hidden" value="51" name="project[repositories_attributes][0][id]" id="project_repositories_attributes_0_id">
to .. 3
Can someone please point out my mistake. And the same problem exist if I use 1 fields_for and loop though an array.
Can anyone please help.
Thanks in advance
Reference to http://apidock.com/rails/v3.2.13/ActionView/Helpers/FormHelper/fields_for
Your code will cause duplication result, because The block given to the nested fields_for call will be repeated for each instance in the collection, so you have to :
<%= form_for #project, :html => {:class => 'project'} do |f| %>
<%= f.label :name, "Project name" %>
<%= f.text_field :name %>
<% #project.repositories.each do |r| %>
<%= f.fields_for :repositories, r do |ff| %>
<%= ff.check_box :repos_name, {} , r.repos_name, true %><%=r.repos_name%>
<% end %>
<% end %>
<%= f.submit "Save"%>
But when create, you should fill the check_box not include in repositories.
So you could:
<%= form_for #project, :html => {:class => 'project'} do |f| %>
<%= f.label :name, "Project name" %>
<%= f.text_field :name %>
<% #project.repositories.each do |r| %>
<%= f.fields_for :repositories, r do |ff| %>
<%= ff.check_box :repos_name, {} , r.repos_name, true %><%=r.repos_name%>
<% end %>
<% end %>
<% (['svn_software','git_software'] - #project.repositories.map(& :repos_name)).each do |name| %>
<%= ff.check_box :repos_name, {} , name, nil %><%=name%>
<% end %>
<%= f.submit "Save"%>
I'm new in rails and need some help creating a Nested form.
I have this in teams/_form.html.erb
<%= form_for #team do |f| %>
<div class="field">
<%= f.label "Name" %><br />
<%= f.text_field :name, :required => true %>
</div>
<%= f.fields_for :players do |builder| %>
<%= render :partial => 'players_field', :f => builder %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
What I want is create a list of players in the team form. The problem is that the render don't work and players_field.html.erb don't render.
<table class="table table-striped">
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Position</th>
</tr>
</thead>
<tbody id="tableRow">
<tr>
</tr>
</tbody>
</table>
TR is added to the table with js.
UPDATE:
Another problem appear now :/
This is my js:
var newRow = document.createElement('tr');
newRow.innerHTML = "<td>"+ counter +"</td>"+
"<%= f.text_field :name %>"+
"<%= f.text_field :position %>"+;
document.getElementById("tableRow").appendChild(newRow);
Error:
undefined local variable or method `f' for #<#<Class:0x0000000288dd38>:0x007f77cc1226e0>
Why I can't add this ?
The block
<%= f.fields_for :players do |builder| %>
<%= render :partial => 'players_field', :f => builder %>
<% end %>
is basically a loop through #team.players. It might be the case that #team simply doesn't have any players to loop through. To show the form for at least one player you can change this to
f.fields_for :players, #team.players.build do |builder| ...
I have a 2 views that I want to use before the create action.
1st view is add_sample_details.html.erb
<% form_tag add_expt_details_samples_path :method => :post do %>
<% for sample in #samples %>
<% fields_for "samples[]", sample do |form| %>
<fieldset>
<legend>Sample Name: <%= sample.name %></legend>
<p><center><%= form.label :sample_title %>
<%= form.text_field :sample_title, :size => 25 %></center></p>
<table>
<tr>
<td>
<%= form.label :taxon_id %>
<%= form.text_field :taxon_id, :size => 15 %>
</td>
<td>
<%= form.label :scientific_name %>
<%= form.text_field :scientific_name, :size => 20 %>
</td>
<td>
<%= form.label :common_name %>
<%= form.text_field :common_name, :size => 15 %>
</td>
</tr>
</table>
<p>
<center>
<%= form.label :sample_descripition %><br \>
<%= form.text_area :description %>
</center>
</p>
</fieldset>
<% end %>
<% end %>
<p><center><%= submit_tag "Next" %></center></p>
<% for samp in #samples %>
<%= hidden_field_tag("sample_ids[]", samp.id) %>
<% end %>
<% end %>
in the SamplesController, the add_expt_details looks like this
def add_expt_details
# Collect the samples that were sent
#expt_samples = Sample.find(params[:sample_ids])
end
and the add_expt_details.html.erb looks like this
<% form_for #expt_samples, :url => create_study_samples_path, :html => { :method => :put } do |f| %>
<% #expt_samples.each do |samp|%>
<% f.fields_for :expts do |form| %>
<%= form.label :experiment_title %>
<%= form.text_field :expt_title, :size => 15 %><br \>
<% end %>
<% end %>
<p><center><%= submit_tag "Next" %></center></p>
<% end %>
and my create_study action is like this
def create_study
#study = Study.new(params[:study])
# this method collects all the samples from the add_sra_details view from the hidden_field_tag
if current_user.id?
# Put the current user_id in the study.user_id
#study.user_id = current_user.id
if #study.save # Save the values for the study
# Update the Sample attributes
#samples = Sample.update(params[:samples].keys, params[:samples].values).reject { |p| p.errors.empty? }
# For all those sample_ids selected by the user, put the study ids in the sample_study_id
#samples.each do |samp|
#study.samples << samp
end
# get the ids_sent from the add_expt_details
#expt_samples = Sample.find(params[:id])
# A Flash message stating the details would be prefereable..! (Means you have to do it)
flash[:success] = "Your Study has been Created"
redirect_to add_expt_details_samples_path
else
flash[:error] = "Study Faulty"
render :action => "add_sra_details"
end
end
end
The error message I get when I keep the #expt_samples = Sample.find(params[:id]) is "undefined method `keys' for nil:NilClass"
and If I dont have the #expt_samples, it gives me an error "Couldn't Find id in Sample"
Can some one suggest how can I update set of sample ids from one form and also create new Expt model records that are associated to the sample_ids in another form.
All suggestions are appreciated.
Cheers
You need to refer to Rails' accepts_nested_attributes_for
To see how to link existing objects with forms, you can see examples here:
http://josemota.net/2010/11/rails-3-has_many-through-checkboxes/
rails 3 has_many :through Form with checkboxes
has_many through checkboxes
On a single page I might have a single Video and then checkboxes to add multiple dogs to it. Easy enough (as follows)...
View:
<%= check_box_tag "video[dog_ids][]", dog.id %>
Controller:
params[:video][:dog_ids] ||= []
But what I can't figure out how to do is have multiple videos, each with multiple dogs.
I currently have this:
<% #videos.each do |video| %>
<%= fields_for "item[]", video do |f| %>
<%= f.hidden_field :id, :index => nil %>
<%= f.text_field :title, :index => nil%>
<%= f.text_area :body, :index => nil %>
<% video.dogs.each do |dog| %>
<%= check_box_tag "item[][video[dog_ids][]]", dog.id %>
<% end %>
<% end %>
<% end %>
But when I do that, dogs_ids is always nil when it's submitted.
Any idea what I'm doing wrong?
Such a setup with fields_for "item[]" and f.text_field :title, :index => nil produces:
<input id="item__name" name="item[][title]" size="30" type="text" value="vid1">
This indicates that the checkbox name should be item[][dog_ids][].
A working example:
<% #videos.each do |video| %>
<%= fields_for "item[]", video do |f| %>
<%= f.text_field :title, :index => nil %>
<% video.dogs.each do |dog| %>
<%= check_box_tag "item[][dog_ids][]", dog.id %> <%= Dog.name %>
<% end %>
<br>
<% end %>
<% end %>
This produces result in params:
{"item"=> [
{"title"=>"vid1", "dog_ids"=>["1", "2"]},
{"title"=>"vid2", "dog_ids"=>["2"]},
{"title"=>"vid3", "dog_ids"=>["2", "3"]}
]}