Using multi-part form with Haml and partials - ruby-on-rails

I have a form divided into two parts (the second part is displayed depending on user's first choice). Note that the second part is in a partial. The two parts must be sent to the same controller.
Using Haml, how can I get all the parameters sent to the controller, using only one submit button ?
ps : I'm using form_tag here
Main file
= form_tag({url: users_search_path}, id:'form1') do
label
= radio_button_tag 'user_type', 'Requestor', false, id: "radio-requestor"
= label_tag 'radio-requestor', "Demandeur"
label
= radio_button_tag 'user_type', 'Contributor', false, id: "radio-contributor"
= label_tag 'radio-contributor', 'Contributeur
.contributor_form
= render partial: 'contributor_search_form'
.requestor_form.hidden
= render partial: 'requestor_search_form'
----------------------------------------------
2nd part (partial contributor_search_form)
= form_tag({url: users_search_path}, id:"form2") do
label
= check_box_tag 'prof', 'prof', false, id: 'prof'
= label_tag 'prof', 'Prof'
label
= check_box_tag 'ticket', 'ticket', false, id: 'ticket'
= label_tag 'ticket', "Ticket"
= submit_tag "Afficher les résultats"

Don't use 2 form_tag if you want to submit them all in 1 click.
If you want the view change base on user interaction without server, you have to do via Javascript
As I guess, you want the param receiving in the controller looks like
{
user_type: "Requestor",
prof: true,
ticket: false
}
so you can write your partial without form_tag as
# contributor_search_form
label
= check_box_tag 'prof', 'prof', false, id: 'prof'
= label_tag 'prof', 'Prof'
label
= check_box_tag 'ticket', 'ticket', false, id: 'ticket'
= label_tag 'ticket', "Ticket"
= submit_tag "Afficher les résultats"
And your main file with partial view being rendered
# Main file
= form_tag({url: users_search_path}, id:'form1') do
label
= radio_button_tag 'user_type', 'Requestor', false, id: "radio-requestor"
= label_tag 'radio-requestor', "Demandeur"
label
= radio_button_tag 'user_type', 'Contributor', true, id: "radio-contributor"
= label_tag 'radio-contributor', 'Contributeur
.contributor_form
= render partial: 'contributor_search_form'
.requestor_form.hidden
= render partial: 'requestor_search_form'
Notice that the partial form has been rendered inside the main form
Now write some Javascript with jQuery
When user select Requestor, show .requestor_form and vice versa for Contributor
Only submit fields within the displayed zone, then you have to disable fields when hidden.
Here is an example
function toggleFormPartial (target, isShow) {
if (isShow) {
target.removeClass("hidden")
// Remove `disabled` attribute for fields within `target`
} else {
target.addClass("hidden")
// Add `disabled` attribute for fields within `target`
}
}
$("#radio-requestor").change(function (e) {
toggleFormPartial($(".requestor_form"), e.val())
})
$("#radio-contributor").change(function (e) {
toggleFormPartial($(".contributor_form"), e.val())
})

Related

First form_tag inside loop never hits the controller action

I have a form inside a loop that looks like this:
- #templates.each do |template|
tr
td.col-md-2
= template.kind.capitalize
td.col-md-9
= form_tag(add_workout_from_template_path, method: :post) do
= hidden_field_tag 'date', params[:date]
= hidden_field_tag 'athlete', params[:athlete]
= hidden_field_tag 'title', template.title
= hidden_field_tag 'kind', template.kind
= hidden_field_tag 'distance', template.distance
= hidden_field_tag 'duration', template.duration
= hidden_field_tag 'notes', template.notes
= hidden_field_tag 'video', template.video
= submit_tag(template.title, class: 'fake-button')
td.col-md-1
= link_to image_tag('cancel.png'),
template_path(template),
method: :delete
For iteration 2, 3 and 4 it works like a charm. Whenever I click template.title the form executes, hits the controller and does what I want. It prints this list and I can click the titles to add them to a calendar:
However, when I click on the first one (Other 20x ...) it never creates what it should in the controller, it just creates a blank object and redirects. When I try to raise in the controller, it never raises on the first one so for some reason it seems like it is not hitting the controller at all?
What am I doing wrong here?
controller code:
def add_workout_from_template
#athlete = User.find_by(slug: params[:athlete])
#workout = Workout.create!(
user: #athlete,
date: params[:date],
title: params[:title],
kind: params[:kind],
distance: params[:distance] || 0,
duration: params[:duration] || 0,
notes: params[:notes],
video: params[:video]
)
redirect_to team_user_workouts_path(current_user.team,
current_user, athlete: #athlete)
end

How to pass parameters to autocomplete_source jquery autocomplete?

I'm using Rails 4.0.2 with jquery-rails (3.1.0) and jquery-ui-rails (4.1.1) gems. I'm added autocomplete in order to do a specific search based on what user typed and other fields at form.
The form:
<%= text_field_tag :field , some_value, data: { autocomplete_source: select_path( { :id => #order.Id , :type => #order.type } ) } %>
Form.js:
$('#field').autocomplete
minLength: 0
source: $('#field').attr('data-autocomplete-source')
select: ( event, ui ) ->
$('#pedido_venda_CodTransp').val(ui.item.value)
$('#transportadora_escolhido').val(ui.item.label)
this.form.submit()
false
...
The controller:
def select
# retrieve parameters
id_cliente = params[:id]
retira_entrega = params[:type]
term = params[:term]
# do the query, etc...
end
When I run the code, everything is OK. The controller receives all parameters and run the query flawlessly.
The parameter type, however, is based on a SELECT control and, in order to change it, I put the following code in the SELECT control.
<%= f.select :type, options_for_select( [['RETIRA','R'],['ENTREGA','E']] , #pedido.RetiraEntrega ) ,{}, { :onchange => "change_type();" } %>
JS Code function:
function change_type()
{
var e = document.getElementById("type");
var option = e.options[ e.selectedIndex ].value;
var field = document.getElementById("field");
var origem = "type=";
source = field.attributes["data-autocomplete-source"].value;
// pesquisa a string retira_entrega=
index = source.search(origem);
field.setAttribute("data-autocomplete-source", source.substring(0,index+origem.length) + String(option));
}
The JS function is called, the last line is run, the attribute is set (I put an alert at the end retrieving the attribute).
The problem is that the controller never receives the changed value (it always receives the value when the form is created).
So, the question is: how can I change a parameter passed on to autocomplete in order to use it in rails controller?
Not sure if this is what your looking for but I was struggling with the same issue because I had two input fields that I wanted different lists loaded to in the autocomplete widget. So what I did was pass an extra param to the auto complete source like this:
<!-- /_form.html.erb -->
<%= f.text_field :auto1, :size => "100", class: "form-control", data: { autocomplete_source: root_path(:fieldType => "numerouno")} %>
<%= f.text_field :auto2, :size => "100", class: "form-control", data: { autocomplete_source: root_path(:fieldType => "numerodos")} %>
Then in my controller I used that extra param to determine which list I needed to show:
if param[:fieldType] == "numerouno"
format.json { render :json => #unoList}
elsif param[:fieldType] == "numerodos"
format.json { render :json => #dosList }
else
flash[:danger] = "Error loading list for autocomplete!"
end
param[:term] still goes through too!

Rails: using partials for inputs is correct?

I'm building a Rails app. I've done all the logic (db, controllers, models, etc). Now its time to make it nice.
In order to centralize the view of the app I was thinking in creating partials for the common stuff. For example one partial called common/_text_input.html.erb that will contain
<div class="field">
<%= f.label id %><br />
<%= f.text_field id %>
</div>
This will be called from inside a form using
<%= render partial: "common/text_input", locals: { f: f, id: :name } %>
Is this approach correct? Is there any other option in rails to do this?
If this is the correct way to do this, how can I acchieve this for a form tag, for example (where content is inserted inside it)?
Thanks
1 - There is another option to do this, Helpers and content_tag:
def text_input(form_builder, attribute, options = {})
options = { div: { class: :field }, label: { class: attribute } }.merge(options) # default options
content_tag :div, options[:div] do
f.label(attribute, options[:label]) + content_tag(:br) + f.text_field(attribute, options[:input])
end
end
Usage:
= form_for #resource do |f|
= text_input(f, :first_name)
= text_input(f, :last_name, input: { style: 'color: red;' }, label: { class: :another_class })
2 - It is correct to do with partials, but it is not as flexible as the Helpers are (see the options hash and the possibility to use another method in specific cases). To handle the form_tag (i.e. no form_builder), you can implement a new method:
# usage
= form_tag root_path, method: :get do
= text_input(nil, :search, input: { value: params[:search] }, label: { content: "Search for something!" })
# helper methods
def text_input(form_builder, attribute, options = {})
options = { div: { class: :field }, label: { class: attribute } }.merge(options) # default options
return text_input_tag(attribute, options) if form_builder.blank?
content_tag :div, options[:div] do
f.label(attribute, options[:label]) + content_tag(:br) + f.text_field(attribute, options[:input])
end
end
def text_input_tag(attribute, options = {})
value = options[:input].try(:delete, :value)
label_content = options[:label].try(:delete, :content)
content_tag :div, options[:div] do
label_tag(attribute, label_content, options[:label]) + content_tag(:br) + text_field_tag(attribute, value, options[:input])
end
end

Dynamically insert ruby generated HTML

I have a form where a user can elect to create more input areas (to provide more information). I have a link the user will click on and then the extra form inputs will be created. I'd like to use the rails form helpers so that I don't have to write the html myself. I've tried inserting the form helpers directly into coffeescript and saving the outputted html to a data tag on the link, but I can't get the coffeescript to execute the ruby code and I'm having escaping issues with the data attribute.
Here's the form:
= simple_form_for([#site, #zone]) do |f|
= f.error_notification
.form-inputs
= f.input :site_id
= label_tag "X"
= text_field_tag 'x_coords[]'
= label_tag "Y"
= text_field_tag 'y_coords[]'
= label_tag "X"
= text_field_tag 'x_coords[]'
= label_tag "Y"
= text_field_tag 'y_coords[]'
= label_tag "X"
= text_field_tag 'x_coords[]'
= label_tag "Y"
= text_field_tag 'y_coords[]'
= link_to "Add Point", "#", id: "add_point", data: { fields: label_tags }
.form-actions
= f.button :submit
When a user clicks the "Add Point" link, I'd like to add another block of:
= label_tag "X"
= text_field_tag 'x_coords[]'
= label_tag "Y"
= text_field_tag 'y_coords[]'
label_tags is in application_helper.rb:
def label_tags
label_tag "Z"
end
The problem is the output for the "Add Point" link is:
Z" id="add_point">Add Point
and the quotation marks are causing the link to come out with the text: "Z" id="add_point">Add Point"
I got the data attribute idea from this screencast
You cannot execute Ruby code from Javascript. When the page is requested all embedded ruby is evaluated and the results are what you get. The issue that I can see from you paste is that your label block is in the right data attribute but it's not escaped.
What you'll need to do is escape the quotes on the generated HTML going into that field and then unescape them via Javascript. You could use html_escape here like: data: { fields: h(label_tags) } (h is an alias for html_escape or you could do this yourself, manually.
def escape(str)
str.gsub(/</, "<").gsub(/>/, ">").gsub(/"/, """)
end
# later in the view for the form
data: { fields: escape(label_tags) }
And then your CoffeeScript would click handler would like:
function unescape(str) {
return str.replace(/((?:<)|(?:>)|(?:"))/g, function($1) {
switch($1) {
case ">":
return ">";
case "<":
return "<";
case """:
return '"';
}
});
}
$("a").on("click", function() {
var html = unescape(this.data("fields"));
$(".the-place-to-put-it").html(html);
});
I do not doubt a better solution exists and as of the posting of this answer I have not tested this to work (in theory it should). Ideally, you should just generate the elements with jQuery in Javascript and not depend on this method for doing this - yes, it's duplicate code duplicated between ruby and Coffee.
Simple solution for me was to replace the double quotes in my generated HTML with single quotes. In code:
= link_to "Add Point", "#", id: "add_point", data: { fields: label_tags.gsub("\"", "'") }
Also, had to use capture and concat in the helper method:
module ApplicationHelper
def label_tags
capture do
concat label_tag "X"
concat text_field_tag 'x_coords[]'
concat label_tag "Y"
concat text_field_tag 'y_coords[]'
end
end
end

radio_button selection

Using Haml 3.1.4 (Separated Sally)
I am curious as to what I am doing wrong.
Why does this not show the first radio button selected?
btw, at execution, #organization.pdf_size does equal 'letter_size'
I would actually like the radio button selected based on the
#organization.pdf_size, however I am just trying to get a hard
coded selection to work atm. tyfyt
= form_for [#organization] do |f|
Select a PDF page size
= label_tag 'Letter (8.5x11)'
= f.radio_button :pdf_size, id: 'letter_size', :checked => true
= label_tag 'Half Legal (8.5x7)'
= f.radio_button :pdf_size, id: 'half_legal_size'
= f.submit 'Save', class: 'button'
I have also tried other examples I have seen on stackoverflow, in this fashion:
= f.radio_button :pdf_size, id: 'letter_size', :checked => #organization.pdf_size == 'letter_size' ? true : nil
Try this:
= f.radio_button :pdf_size, "value", id: 'letter_size', :checked => true
As documented here, the radio button needs a value before the options.
Remember to change "value" to something that makes sense for your application.
Try appending this to your link:
input_html: {checked: true}

Resources