RoR: Nested forms and dynamic fields - ruby-on-rails

I am struggling with some nested forms which dynamically inserts fields. I have based my code on Ryan Bates great railscasts. My problem though is that I need to move my "add fields" button outside the nested form. This causes problems because I don't have access to some of the objects required to generate the form.
In the code below the add/remove button is in the same location. I can change the if statement to do this, but I cannot move the add fields button outside the f.simple_fields_for :fund_levels loop.
Any suggestions for a clean way of doing this?
The view
= f.simple_fields_for :fund_levels do |fl|
- fl.object.id ? headerappend = fl.object.id : headerappend = "#{i}-notset"
%div{id: "flheader-#{headerappend}"}
%div
= link_to "Sponsor levels", "#", :class => "show_hide", :id => "initiator1_fl#{i}", :style=>"margin-bottom:2px;", :onclick=>"return false"
%div{:class => "slidingDiv #{fund_levels_last?(i, #fund_level_count) ? "shown": "hidd" } whitebg leftadj", :id=>"body_fl#{i}", :style=>"width:100%;" }
= render :partial => 'fund_level_fields', :locals => {:fl => fl, :ad => ad, :i => i, :f => f}
%div{:style => "float: right; padding: 20px 20px 20px 0"}
= fund_levels_last?(i, #fund_level_count) ? ( link_to_add_fields "add new level", f, :fund_levels, fl, i+1, ad ) : ( link_to "remove", accounts_ad_fund_level_path(ad, fl.object.id), {:class => 'button orange sm', :method => :delete, :remote => true, :confirm => t('q.are_you_sure')} )
- i += 1
%div#newfields
- # THE BUTTON SHOULD GO HERE if it is an ADD FIELDS (remove btn should stay above)
the application_helper file
def link_to_add_fields(name, f, association, sublevel, counter, parent)
new_object = f.object.class.reflect_on_association(association).klass.new
fields = f.fields_for(association, new_object, :child_index => "new_#{association}") do |builder|
render(association.to_s.singularize + "_fields", :f => builder, :fl => sublevel, :i => counter, :ad => parent) #fl, i, ad
end
# this bit is required because of some bugs in haml
fields.gsub!(/\<haml\:newline\/\>/, '').html_safe
link_to name, '#', :id => "#{name}|-|#{association}|-|#{fields}", :class => "addfields", :onclick => "return false"
end

Solved by storing the last loop object to a variable and use that outside the loop.
(...) -thisvar = fl
-link_to_add_fields "add new level", f, :fund_levels, thisvar, i+1, ad

Related

How can I pass params using link_to?

I want to pass a parameter using link_to. (Also I am trying to use Bootstrap tab)
ilban.html.erb
<%= link_to '일반공지', '#home', { 'data-toggle' => 'tab', 'aria-controls'=>'home', 'role'=>'tab', :where => 1 } %>
cpu_controller.rb
#where = params[:where]
This code doesn't get where as an parameter. How can I fix it?
I have not tested it, but it should pass the params that you want.
link_to "Search", searches_path(:where => 1, :when => "Today"), { 'data-toggle' => 'tab', 'aria-controls'=>'home', 'role'=>'tab' }
Controller:
#where = params[:where]
In Rails 5, try this syntax for link_to
link_to 'Show', view_path(:id => view.id), { 'data-toggle' => 'tab', 'aria-controls'=>'home', 'role'=>'tab' }
In the place of view path you can edit with your controller path and pass the valid id that you need to link.
Or, try this syntax also to pass params
<%= link_to "Add car", {:controller => "car", :action => "add_car", :car => car.id }%>
And add in your controller
#car = Car.find(params[:car])

Proper syntax for controller action with slim

Rails 3.2
I have a view views/tickets/show.html.slim with a number of sections. I want to have different controllers for each section, and have actions like New Save Edit
So in my views/tickets/show.html.slim, I have:
- #customer_info = customer_info #ticket
h4.form-header Customer Information
.form-section.attachments
- if #customer_info.nil?
= render partial: 'tickets/sections/customer_info', locals: {ticket: #ticket }
In my view, I have:
= form_for CustomerInfo.new do |f|
- f.hidden_field :ticket_id, :value => ticket.id
.form-horizontal-column.customer-info
.form-group
= f.label :first
= f.text_field :first, maxlength: 50
.form-group
= f.label :last
= f.text_field :last, maxlength: 50
- logger.info("Marker 1")
.actions = link_to "Save", :controller => :customer_infos, :action => :create
- logger.info("Marker 2")
.clear
When I run the application in test mode, and I select a ticket, I get the following response:
Incomplete response received from application
In my test.log file, I have:
CustomerInfo Load (0.1ms)[0m SELECT customer_infos``.* FROMcustomer_infosWHEREcustomer_infos.ticket_id` = '1466026127' LIMIT 1
Marker 1
Rendered tickets/sections/_customer_info.html.slim (11.6ms)
Rendered admin/tickets/show.html.slim within layouts/application (563.0ms)
There is no Marker 2
If I replace:
.actions = link_to "Save", :controller => :customer_infos, :action => :create
With:
.actions = f.submit 'Save'
Then the form renders fine.
Any idea why this is not working?
Attempted fix:
In my tickets/section/_customer_info.html.slim, I did:
.actions = link_to "Save", create_customer_info_path, method: :post
In my routes.rb, I have:
post '/customer_infos/create' => 'customer_infos#create', as: 'create_customer_info'
I am now getting the following error message:
undefined method `customer_infos_path' for #<#<Class:0x00000008bb54d8>:0x00000009df3c30>
Where is customer_infos_path coming from?
If I do rake routes, I get:
create_customer_info POST /customer_infos/create(.:format) customer_infos#create
According to the Rails docs, using the controller option is frowned upon. You should set up your route with a name, like this:
post '/customers/create' => 'customer_infos#create', as: 'create_customer'
Then your view should look something like this:
.actions
= link_to "Save", create_customer_path, method: :post
Hope this helps!

Locals not set when rendering multiple partials from the same controller

This bug has me perplexed.
I am displaying a Widget with the last 5 data points and a form for a new data_point. When I try to render both the form and the display partial it fails with a routing error. If I comment out either partial it works fine. So with only the form partial it works, or with only the display partial it works fine with the other.
Three relevant models/controllers: Widget, DataSet and DataPoint. DataPoints is STI so I have different partials to display the different types that match the class name.
When both render :partial => ... are uncommented it looks as if the data_point being passed to the second partial is the same as the data_point being passed to the first partial. It is all nil with only a data_set_id. I tried renaming the local from :data_point to :dp in case it was a name collision.
View, both partials and the routing error shown at the bottom.
Is there a limitation on passing :locals to partials when you are trying to render more than one partial in a view?
View: show.html.haml (the show action on the controller is empty)
- title "#{#widget.type.underscore.humanize} for #{#widget.data_set.name.to_s.downcase}"
.row
.span6
= render :partial => 'data_points/form', :locals => { :data_set => #widget.data_set, :data_point => #widget.data_set.data_points.build }
%br
- last_5 = #widget.data_set.data_points.last(5)
- if last_5.count > 0
%h3= "Last #{pluralize( last_5.count, 'Entry' )}"
- last_5.each do |dp|
%pre
= dp.inspect
= render :partial => "data_points/#{#widget.data_set.data_type.underscore}", :locals => { :data_set => #widget.data_set, :data_point => dp }
- else
%h3 No data yet!
= form_tag( push_widget_path #widget, :method => 'POST' ) do
= submit_tag( "Push Data", :class=>"btn btn-primary" )
= link_to "Edit your data", data_set_path( #widget.data_set ), :class=>'btn'
.span6
= image_tag "widgets/#{#widget.type.underscore}.png"
%p
Using the
%b= link_to #widget.data_set.name, #widget.data_set
data set.
= render :partial => "widgets/#{#widget[:type].underscore}", :locals => { :widget => #widget } rescue nil
%p
= link_to 'Edit', edit_widget_path(#widget), :class=>'btn'
Form Partial (data_points/_form.html.haml)
%pre
= data_set.inspect
%pre
= data_point.inspect
= form_for [data_set,data_point], :html=>{:class => 'well form-horizontal'} do |f|
-if data_point.errors.any?
#error_explanation
%h2= "#{pluralize(data_point.errors.count, "error")} prohibited this data_point from being saved:"
%ul
- data_point.errors.full_messages.each do |msg|
%li= msg
= render :partial => "data_points/#{data_set.data_type.underscore}_form", :locals => { :f => f, :data_point => data_point, :data_set => data_set }
= f.text_field :created_at, :type=>'datetime'
.form-actions
= f.submit 'Save'
Display Partial (data_points/_multi_value_data_point.html.haml)
.data-point{ :id=>"data-point-#{data_point.id}"}
%span.value
- data_point.data.map do |k,v|
- label = data_set.properties[:data_series][k][:name]
%span.key= "#{label}: "
= v
.meta
= data_point.created_at
%span.edit
= link_to 'Edit', edit_data_set_data_point_path( data_set, data_point )
%span.delete
= link_to 'Destroy', [data_set, data_point], :method => :delete, :data => {:confirm => 'Are you sure?'}
=# link_to 'Edit', '#', id: "edit-data-point-#{data_point.id}"
:javascript
$("#edit-data-point-#{data_point.id}").click( function(e) {
$("#data-point-#{data_point.id}").html( "#{escape_javascript( render :partial => "data_points/multi_value_data_point_form", :locals => { data_set: data_set, data_point: data_point } )}");
return false;
});
Routing Error
Routing Error
No route matches {:action=>"edit", :controller=>"data_points", :data_set_id=>#<MultiValueDataSet id: 4, ... properties removed ... type: "MultiValueDataSet">, :id=>#<DataPoint id: nil, data: {}, data_set_id: 4, created_at: nil, updated_at: nil>}
Try running rake routes for more information on available routes.
The problem stems from the fact that the form is building a new element that isn't saved, and last is returning that unsaved element.
Simple solution, replace .last(5) with .find( :all, order: 'id desc', limit: 5 )

Parameter in AJAX request

I have a view which contain multiple links:
<% a.each do |q| %>
<%= link_to "stock it",
{ :action => "stock",
:qid => q.question_id,
:qur => q.question_answers_url,
:qti => q.title } ,
:remote => true %>
<div id="<%= "stock" + q.question_id.to_s %>"></div>
<% end %>
Each link generate AJAX-request. Here is a controller:
def stock
if(!Later.where(:question_id => params[:qid]).exists?)
later = Later.new(:question_id => params[:qid], :name => params[:qti], :url => params[:qur])
later.save
end
respond_to do |format|
format.js { render :layout=>false }
end
end
Now return to the view. Each link has a 'div' with unique id='stock'. When user press the link I need to add text to specific div with corresponding id.
I have a stock.js.erb file:
$("#stock<number>").html("some text");
How can I pass div-id to stock.js.erb and how can I use it ?
Common use is to add object.id to your DOM id. That what you exactly did:
<div id="<%= "stock_#{q.question_id}" %>"></div>
Then in your controller you shoud define your question_id or your exact question:
def stock
if(!Later.where(:question_id => params[:qid]).exists?)
later = Later.new(:question_id => params[:qid], :name => params[:qti], :url => params[:qur])
later.save
end
#question_id = params[:qid]
end
Now it will be shared with your stock.js.erb file:
$("#stock_<%= #question_id %>").html("some text");

Will_Paginate is it possible to let the user select the # of paginated records?

I have a paginated list, and a select box of numbers showing how many pages to display. When the user picks a value I want the screen to refresh with the resized list.
View:
<%= select_tag :paginate_size, options_for_select([['Display: 5', 5],['Display: 10',10],['Display: 20', 20],['Display: 30', 30],['Display: 50', 50],['Display: 100',100]]), :onchange => remote_function(:url => {:action => :show_my_entries}, :with =>"'paginate_size='+value")%>
When I select a value I can see in the log that the show_my_entries function is being called and sent the paginate_size parameter.
In the controller I set the per_page variable as shown below, however selecting anything in the list doesn't refresh the screen with the new list size:
def show_my_entries
if (params[:paginate_size].nil?)
paginate_size = 8
else
paginate_size = params[:paginate_size]
end
#entries = Entry.find_all_my_entries_by_pub(session[:publication_id], session[:user_id])
#entries_paginate = Entry.paginate :page => params[:page], :order => 'title', :per_page => paginate_size, :conditions => "publication_id = " + session[:publication_id].to_s + " AND writer_id = " + session[:user_id].to_s
render :layout => "dashboard"
end
You're missing the :update parameter in the remote_function which should point to the id of the HTML element containing the entries list.
So, if the content is, for example, done like this:
<div id="my_entries">
<%= print_entries_list %>
</div>
Your remote function should be like this:
remote_function(:update => "my_entries", :url => {:action => :show_my_entries}, :with =>"'paginate_size='+value")

Resources