Rails: Iterate through attributes with simple_form - ruby-on-rails

I have a rails model with a large number of fields (112), for loading a configuration. I would like, in the edit and show forms, display only if the field is already filled. That is, if the field is null in the database then do not display it for edit.
There are two records - the main one is Batch and there is a 1:1 relationship to the Primer3Batch class. I'm trying to do an edit on the Primer3Batch class on the show action of Batch, I'm not sure if this is a good idea or will even work.
I have tried using the attributes method and am getting this error:
undefined method `attributes' for #<SimpleForm::FormBuilder
batches_controller.rb
def show
#batch = Batch.find(params[:id])
#primer3 = Primer3Batch.where(:batch_id => #batch.id)[0]
respond_to do |format|
format.html # show.html.erb
format.json { render json: #batch }
end
end
batches/show.html.erb
<h1>Batch Details: <%= #batch.id %></h1>
<%= simple_form_for(#primer3) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<% f.attributes.each_attribute do |a| %>
<% if a %><%# if a is not nil %>
<%= f.input a %><%# send the field to the form %>
<% end %>
<% end %>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>
EDIT
Thank you JSWorld for pointing out the error of using the instance variable. I have corrected it and seem to have gotten further but it's still not quite right. This is the line changed - note attributes.each as attributes.each_attribute doesn't work.
<% #primer3.attributes.each do |a| %>
Now I am getting an error on the form fields:
undefined method `["id", 110]' for #<Primer3Batch:
I guess I need to somehow turn this :
a ["id", 110]
into:
<%= f.input :id %>
* EDIT 2 *
Final code block based on IIya Khokhryakov's answer.
<%= simple_form_for(#primer3) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<% #primer3.attributes.each_pair do |name, value| %>
<%= f.input name if value %>
<% end %>
</div>
<div class="form-actions">
<%= f.button :submit %>
</div>
<% end %>

I hope your intention is #primer3.attributes and not f.attributes. The error is because f is the form object and does not have attributes associated with it.

#primer3.attributes is a Hash of the model's attributes and their values. So you can do something like this:
<% #primer3.attributes.each_pair do |name, value| %>
<%= f.input name if value %>
<% end %>

Related

Ruby On Rails - creating an object does not respect validations and showing error messages

I'm having troubles handling objects that not respect the validation.
I'm building an app in which an user can create a "Trip" model and then add steps to his trip, that I called "Traces". Each added trace prints a new part of a map present in the trip#show action.
The association between models is user has_many trips and trip has_many traces
In the users#show I put a "CREATE NEW TRIP" button linking to the trips#new and here I have the form_for with the field corresponding to the Trip attributes.
When I fill the form correctly everything is ok. When something is missing or wrong (for the validations) I get this error:
NoMethodError in Trips#create
undefined method `model_name' for Array:Class
------ in the trips_controller.rb
def create
#trip = current_user.trips.build(params[:trip])
if #trip.save
# handle a successful save
flash[:success] = 'Trip created!'
redirect_to user_trip_path(#trip, user_id: current_user.id)
else
#trip = []
#feed_items = []
render 'new'
end
end
------ in app/views/trip, in the new.html.erb
h1>Create a trip</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3 general-input">
<%= form_for ([current_user, #trip]) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :name,'Give a name to your trip ;)' %>
<%= f.text_field :name %>
<%= f.label :trip_start, 'Choose your starting point!' %>
<%= f.text_field :trip_start %>
<%= f.label :departure, 'When are you leaving?' %>
<%= f.date_select :departure, :start_year => Date.current.year %>
<%= f.label :arrive, 'And coming back home?' %>
<%= f.date_select :arrive, :start_year => Date.current.year %>
<%= f.submit 'Create a new trip', class: 'btn btn-primary btn-lg' %>
<% end %>
EDIT 1: problem solving removing #trace=[ ] from trips_controller.rb
EDIT 2:
I also have a similar problem with the creation of a new Trace:
The form for adding a new trace is in the trip#show page.
When I try to create a trace that not respects the validation (e.g. if I leave blank the "destination" field) I get this error:
NoMethodError in Posts#create
undefined method `any?' for nil:NilClass
When I'm on the Trip page where the form for the Traces is placed, the URL is like:
http://localhost:3000/users/2/trips/8
but when I create a not valide Trace it switchs to a path like:
http://localhost:3000/trips/8/posts
I suppose I'm doing something wrong handling the error messages. I probably misunderstood something, even because I'm new to Rails and web programming in general.
Here you are some code parts, hoping it helps to understand my mistake:
------ in the traces_controller.rb
def create
#trip= Trip.find(params[:trip_id])
#trace = #trip.traces.create(params[:trace])
if #trace.save
flash[:success] = 'Trace created!'
redirect_to user_trip_path(#trip, user_id: current_user.id)
else
#trace=[]
render 'trips/show'
end
end
------ in app/views/shared, in the add_trace_form.html.erb
<p>Complete your trip adding a route!</p>
<%= form_for ([#trip, #trip.traces.build]) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="block post-form">
<div class="field ">
<%= f.text_field :end, placeholder: 'Where are you going next?' %>
<%= f.label :arr_day, 'When?' %>
<%= f.date_select :arr_day, :start_year => Date.current.year %>
<%= f.submit 'Add route', class: 'btn btn-primary btn-landing' %>
</div>
</div>
<% end %>
------ in app/views/shared, in the error_messages.html.erb
<% if object.errors.any? %>
<div id="error_explanation">
<div class="alert alert-error">
The form contains <%= pluralize(object.errors.count, "error") %>.
</div>
<ul>
<% object.errors.full_messages.each do |message| %>
<li>* <%= message %></li>
<% end %>
</ul>
</div>
<% end %>
------ in the routes.rb
resources :users do
resources :trips
end
resources :trips do
resources :traces
end
resources :traces
Thanks a lot
i think when you are passing the f.object in locales in render its is passing array not the active record object ,<%= render 'shared/error_messages', object: f.object %>.
Can u check inspecting object in your partial and what class it has.
Try inspecting object.errors.inspect
Try refering http://guides.rubyonrails.org/layouts_and_rendering.html#using-partials

How do I separate elements by their type with an each method?

I created two scaffolds: announce_sections and announcements. The announce_sections are the types of announcements there are (i.e. games, tryouts, etc) and when I create an announcement I specify what type of announce_sections it is. I'm trying to display it so that each announce_section is viewed, with each announcement and its information under the announce_section. This is what I came up with:
<% #announce_sections.each do |announce_section| %>
<%= announce_section.name %>
<% #announcements.each do |announcement| %>
<%= announcement.announcement_title %>
<%= announcement.information %>
<%= announcement.additional_information %>
<%= announcement.type %>
<% end %>
<% end %>
However, this code only displays the announce_sections with the all announcements under it. The announcements don't get separated into their respective announce_sections. How do I change it so that it does?
<% #announce_sections.each do |announce_section| %>
<%= announce_section.name %>
<% #announcements.where(type: announce_section).each do |announcement| %>
<%= announcement.announcement_title %>
<%= announcement.information %>
<%= announcement.additional_information %>
<%= announcement.type %>
<% end %>
<% end %>
Use the name of the field you are using to assign the announcement type instead of 'type'
There are many ways to solve this, but one simple one is to build a hash where the key is the type of announcement_section and the value is an array (or Set) of the announcement. One way to build that hash is to use the Hash.new {|hash, key| ... } form of the constructor.
#hash = Hash.new {|hash, section| hash[section] = Array.new }
#announcements.each do |a|
# for each announcment append it to the array under the hash
#hash[a.section] << a
end
And then, in the view
<% #hash.keys.each do |section| %>
<%= section %>
<% #hash[section].each do |announcement| %>
<%= announcement.announcement_title %>
<%= announcement.information %>
<%= announcement.additional_information %>
<%= announcement.type %>
<% end %>
<% end %>

Undefined method - passing a value from one view to another

I have a form in a view similar to this:
<%= form_tag("mystuff/new", method: "get") do %>
<% #accesses.each do |access| %>
<%= radio_button_tag(:cost, access.cost) %>
<%= label_tag :label,access.label, :class=>"control-label" %>
<%= label_tag :costlabel, access.cost, :class=>"control-label" %> <%= label_tag :costlabel2, "Euros", :class=>"control-label" %>
<% end %>
<%= submit_tag "Go" %>
<% end %>
My controller has:
helper_method :amount
def new
#amount = params[:cost]
end
and the view of mystuff/new has a line like:
<div class="row-fluid offset1">Stuff: <%= amount -%> </div>
I get Error undefined method `amount'. What I want to do is pass the value of the radio button to the next view, but I don't want to use a database. What's the proper way to do that in rails?
You are missing a # here. #amount.

Capturing a nil object and rendering a view

I seem to be unable to capture a nil response from my api call, when there are no results I get this error
undefined method `[]' for nil:NilClass
So if this happens I would like to show a message like 'No book found', so i have tried these
<% if #results.empty? %>
<% if #results.nil? %>
<% if #results.nil %>
but none of them capture the error
Controller
def results
results = book_search(params[:search])
#results = results
#book = Book.new
#book.author = results.first["artistName"]
#book.bookname = results.first["trackName"]
#book.image = results.first["artworkUrl100"].gsub("100x100-75.jpg", "200x200-75.jpg")
#book.description = results.first["description"]
end
View
<div class="container">
<div class="row">
<div class="span8 offset2">
<% if #results.nil? %>
<h1 class="resultTitle">sorry we can't find anything for that book.</h1>
<%= link_to "Back To Search", root_path, :class => 'backButton' %>
<% end %>
<%= #results.first["trackName"] %> <br>
<img src =<%= #results.first["artworkUrl100"] %>> <br>
<%= #results.first["artistName"] %> <br>
<%= truncate(remove_tags(#results.first["description"]), :length => 250) %> <br>
<%= form_for #book do |f| %>
<%= f.label :category_id, "Category" %>
<%= f.collection_select(:category_id, Category.all, :id, :name, :prompt => 'Please select a Category') %>
<%= f.hidden_field :author %>
<%= f.hidden_field :bookname %>
<%= f.hidden_field :image %>
<%= f.hidden_field :description %>
<%= f.submit 'Save book' %>
<% end %>
</div>
</div>
</div>
Any ideas on what it could be
Thanks
The latter version will return true if #results is nil. You have some other error elsewhere.
If the error still shows up, then you access results even when #results.nil? returns true. More specifically take a look at the places where you call the square bracket operator [] as the error is caused by one of them.
EDIT: after discussion in chat we have discovered the problem. First in the conroller book_search returns an empty array and then, when invoking results.first["artistName"] this causes the error, because results.first is nil. So to fix that we added the following to the controller:
def results
results = book_search(params[:search])
#results = results
unless #results.empty?
#book = Book.new
#book.author = results.first["artistName"]
#book.bookname = results.first["trackName"]
#book.image = results.first["artworkUrl100"].gsub("100x100-75.jpg", "200x200-75.jpg")
#book.description = results.first["description"]
end
end
I.e. a check to only create a new book and set its properties if a book was found. Now in the view, a check should be added to only render the rest of the view(the part that invokes [] operator on #results if results is not empty. This is achieved using an else statement:
<div class="container">
<div class="row">
<div class="span8 offset2">
<% if #results.empty? %>
<h1 class="resultTitle">sorry we can't find anything for that book.</h1>
<%= link_to "Back To Search", root_path, :class => 'backButton' %>
<% else %> <--- I've putted an else instead of end here!!!
<%= #results.first["trackName"] %> <br>
<img src =<%= #results.first["artworkUrl100"] %>> <br>
<%= #results.first["artistName"] %> <br>
<%= truncate(remove_tags(#results.first["description"]), :length => 250) %> <br>
<%= form_for #book do |f| %>
<%= f.label :category_id, "Category" %>
<%= f.collection_select(:category_id, Category.all, :id, :name, :prompt => 'Please select a Category') %>
<%= f.hidden_field :author %>
<%= f.hidden_field :bookname %>
<%= f.hidden_field :image %>
<%= f.hidden_field :description %>
<%= f.submit 'Save book' %>
<% end %>
<%end %> <--- Added an end for the if-else here.
</div>
</div>
</div>
So I have added an else and an end that I have indicated in the code. Why do you need to do that? Well because even after performing the check and displaying the message, the rest of the page was still evaluated and this bit: %= #results.first["trackName"] %> <br> was causing the error. You should not try to call the [] operator if you know #results is empty. The new version only displays the remaining part of the page in the else case i.e. if #results is not empty.
Hope this makes it clear.

" undefined method `enumerable_enumerator_path' " error

I'm using basic scaffold structure. What I need, is to add 'moderate' action and view by changing published to true. In my idea, on moderate.html I should get the list of all unpublished entries with the ability to change and save their parameters.
Here are parts of my code:
#names_controller.rb
def moderate
#name = Name.find(:all, :conditions => {:published => false} )
respond_to do |format|
format.html
format.xml
end
end
#moderate.html.erb
<% form_for #name.each do |f| %>
<%= f.error_messages %>
<%= f.text_field :which %>
<%= f.text_field :what %>
<%= f.check_box :published %>
<%= f.submit %>
</p>
<% end %>
Instead I'm getting this error:
NoMethodError in Names#moderate
Showing app/views/names/moderate.html.erb where line #1 raised:
undefined method `enumerable_enumerator_path' for #<ActionView::Base:0x1042c3e90>
Extracted source (around line #1)
So, can you help to newbie please?
ruby 1.8.7 (2009-06-12 patchlevel 174)
[universal-darwin10.0] Rails 2.3.5
If you want to update each name in a separate form, then all you need to do is move the loop above form_for:
<% #name.each do |n| %>
<% form_for n do |f| %>
<%= f.error_messages %>
<%= f.text_field :which %>
<%= f.text_field :what %>
<%= f.check_box :published %>
<%= f.submit %>
</p>
<% end %>
<% end %>
But if you'd like to do it all in one submit (a single form) then I guess you can't use form_for. I'd use form_tag to create a custom form to update multiple instances. This should work both for create and edit form:
<%= form_tag moderate_names_path do %>
<% #names.each do |name| %>
<fieldset>
<%= fields_for "name[#{name.id}]", name do |name_fields| %>
<p><%=name_fields.label(:this)%>: <br /><%= name_fields.text_field :this %></p>
<p><%=name_fields.label(:that)%>: <br /><%= name_fields.text_field :that %></p>
<p><%= name_fields.check_box :published %> <%=name_fields.label(:published)%></p>
<% end %>
</fieldset>
<br />
<% end %>
<%= submit_tag %>
<% end %>
NOTICE: I changed #name to #names in the second example

Resources