Ruby on Rails and how to render partial using json and jquery - ruby-on-rails

Ruby on Rails newbie whose confused and frustrated :) I've spent over a day on this and think I've probably just confused myself.
Basically, I'm trying to render a partial in a view. Here's what I have specifically:
A form with 2 basic fields: Category and SubCategory. SubCategory changes depending on what the user selected in Category. I'm using "JQuery" with the assets pipeline enabled. This part works:
contact_infos.js.coffee
jQuery(document).ready(->
$("select#contact_info_category_id").change(->
id_value_string = $(#).val()
if id_value_string == ""
# if the id is empty remove all the sub_selection options from being selectable and do not do any ajax
$("select#contact_info_subcategory_id option").remove()
row = "" + "" + ""
$(row).appendTo("select#contact_info_subcategory_id")
else
# Send the request and update sub category dropdown
tmp = '/subcategories/for_categoryid/' + id_value_string + '.json'
$.ajax(
type: 'GET',
dataType: 'json',
url: tmp,
timeout: 2000,
error: (XMLHttpRequest, errorTextStatus, error) -> alert "Failed to submit : " + errorTextStatus + " ;" + error,
success: (data) ->
# Clear all options from sub category select
$("select#contact_info_subcategory_id option").remove()
# put in a empty default line
row = "" + "" + ""
$(row).appendTo("select#contact_info_subcategory_id")
# Fill sub category select
$.each(data, (i, j) ->
row = "" + j.name + ""
$(row).appendTo("select#contact_info_subcategory_id")
)
)
)
)
It generates a json response correctly.
When the form loads, in addition to Category and SubCategory, I also have 2 text fields - previous_value and current_value; however, if
SubCategory == "Full"
then I hide previous_value and current_value and need to insert a partial with new fields.
I'm having no problem hiding previous_value and current_value fields with JQuery works and looks like this (this is inserted into the code above):
$("select#contact_info_subcategory_id").change(->
id_text_string = $("#contact_info_subcategory_id option:selected").text()
if id_text_string == "Full"
$('#contact_info_previous_value_id').hide()
$('#contact_info_current_value_id').hide()
else
$('#contact_info_previous_value_id').show()
$('#contact_info_current_value_id').show()
)
I created a div called "test" in my form where I want to insert the new fields if SubCategory is "Full" and of course, inserting this line into the contact_infos.js.coffee doesn't work:
$('#test').html('<%= escape_javascript render("contact_infos/_full_name_info") %>')
as all I get on the page is the string "<%= escape_javascript render("contact_infos/_full_name_info") %>"
I've tried the following but can't get any to work:
1. creating a new.json.erb file with the following code:
<% self.formats = ["html"] %>
test = {
"html":"<%= raw escape_javascript(render :partial => 'contact_infos/full_name_info',
:content_type => 'text/html')}
%>"
}
This json file never triggered. My controller has this line:
format.json { render json: #contact_info }
Is this the best way to do this? If yes, what can I try next?
2. I saw a posting yesterday (I can't find it now - was on another computer) about creating a javascript variable (I called it fullnamefield) in the application.html.erb layout file as well as adding the js variable to the new.html.erb view, which I did. I also added this line to the contacts_infos.js.coffee:
('#test').html(fullnamefield)
and it worked!! EXCEPT that then when I went to any other area of the site, I got an error.
3. As a workaround, I thought about trying to change the json that my jquery produces to a js and then trying to trigger the new.js.erb. I ran into trouble trying to convert the ajax call. I could create "json" and also "text" dataTypes but not script (not sure why).
So... any ideas/help? I've really been searching and I'm frustrated enough that I'm considering just creating all the fields and hidings/showing them as needed from JQuery, which would be so simple to implement but is just wrong.
UPDATE: Attempt 4 (or is it 40?):
What you wrote got me thinking... I think I'm close but not there yet.
In my _form.html.erb, I added to the Subcategory field data-remote, data-url and data-type:
<div class="field">
<%= f.label :category_id %>
<br/>
<%= collection_select(:contact_info, :category_id, Category.all, :id, :name, options ={:prompt => "--Select a Category--"}) %>
</div>
<div class="field">
<%= f.label :subcategory_id %>
<br/>
<%= collection_select(:contact_info, :subcategory_id, Subcategory.find_all_by_category_id(#contact_info.category_id), :id, :name, options ={:prompt => "--Select a SubCategory"}, "data-remote" => true, "data-url" => "/contact_infos/get_full_fields", "data-type" => :json ) %>
</div>
Then in the contact_infos_controller.rb I added:
def get_full_fields
#full_name = FullName.new
respond_to do |format|
format.js
end
end
In my routes.rb I modified contact_infos by adding collection do...
resources :contact_infos do
collection do
get 'get_full_fields'
end
end
I created contact_infos\get_full_fields.js.erb:
var full_fields_form = $('<%= j(render(:partial => "contact_infos/full_name_info"))%>');
$('#test').html(full_fields_form);
Now when I test this in the browser with debugger and change SubCategory to "Full" I can see that it runs correctly (I think) in that I'm getting this back:
Request URL:http://localhost:3000/contact_infos/get_full_fields?contact_info%5Bsubcategory_id%5D=3
Request Method:GET
Status Code:200 OK
The "Type" is showing up as "text/javascript." The Response tab is just showing the javascript code but nothing is happening/triggering. Even when I place just a
alert('hello');
in the js file nothing happens.
Any ideas why?

Why not do it the same way you get subcategory data? Create a view containing the partial (and corresponding controller action) and call it via ajax when you want to display that content.

Related

I18n translated values do not show up if changed from helper method rails

Hello not sure how to explain this but i will give it my best shot.
I hv a model like this
class Answer < ActiveRecord::Base
EMPLOYMENT_TYPE = [
['Employed', I18n.t('self_evaluation.self_evaluation_form.employment_status.employed')],
['Self-Employed', I18n.t('self_evaluation.self_evaluation_form.employment_status.self_employed')],
['Unemployed / Retired', I18n.t('self_evaluation.self_evaluation_form.employment_status.unemployed')]
]
end
I also have a helper method that calls this
module AnswerHelper
def get_employment_type_list
return Answer::EMPLOYMENT_TYPE
end
end
And a partial view that should display the values like this
_step_1.html.erb
<% employment_list = get_employment_type_list %>
<%= employment_list %>
<div class="flex_center flex_column button-wrap">
<%= collection_radio_buttons("application[answer_attributes]", :employment_type, employment_list, :first, :last, item_wrapper_tag: false, checked: #selected_employment_type) do |b|
b.label(class: 'employment_type_radio') { b.radio_button(:onchange => "return_employment_type(value)", class:'radio-label') + b.label(class:'button-label'){b.text}}
end %>
</div>
_step_2.html.erb
<% employer_list = get_employer_list(chosen_employment_type(#decoded_self_evaluation)) %>
<% employer_type = #decoded_self_evaluation.try(:loan_application).try(:kyc_answer).try(:employer_type) %>
<div class="flex_center flex_column button-wrap">
<%= collection_radio_buttons("loan_application[kyc_answer_attributes]", :employer_type, employer_list, :first, :last, item_wrapper_tag: false, checked: employer_type) do |b|
b.label(class: 'employer_type_radio') { b.radio_button(:onchange => "is_business_registered(value)", class:'radio-label') + b.label(class:'button-label'){b.text}}
end %>
</div>
identity.js
$.ajax({
url:'/self_evaluations/' + self_evaluation_id + '/employment_type',
method:"POST",
data:JSON.stringify({employment_type: employment_type, key: $('#token_key').val()}),
contentType:"application/json; charset=utf-8",
dataType:"script",
async: false,
success: function(data) {
$valid = true;
}
})
employment_list.js.erb
$('#id_employer').empty();
$('#id_employer').append("<%=j render 'self_evaluations/self_evaluation_partials/step_2' %>");
Now the problem is that i have already added all that's needed for I18n translations to happen in Rails and for texts that are translated on the html view it works correctly when i switch from the different languages but for texts that are been gotten via a helper method like this. Based on initial help, on initial load, all page work fine but if i change the language and then run an ajax call that loads the employment_list.js.erb my locale seems to still be the primary language the the values do not change accordingly
Not sure why this is happening anyone faced something like this before?
Because you declared the translations as a constant in your model, so the translations were loaded just one time when Answer model was loaded in the first time.
In production environment, the model will be loaded after starting server. In development environment, the model will be loaded when the file content is changed, ...
In order to solve your issue, you can add the translations in your helper instead:
module AnswerHelper
def get_employment_type_list
[
['Employed', I18n.t('self_evaluation.self_evaluation_form.employment_status.employed')],
['Self-Employed', I18n.t('self_evaluation.self_evaluation_form.employment_status.self_employed')],
['Unemployed / Retired', I18n.t('self_evaluation.self_evaluation_form.employment_status.unemployed')]
]
end
end
I usually use this approach, please have a look!
class Answer < ActiveRecord::Base
EMPLOYMENT_TYPE = [:employed, :self_employed, :unemployed_or_retired]
end
module AnswerHelper
def get_employment_type_list
i18n_scope = 'self_evaluation.self_evaluation_form.employment_status'
Answer::EMPLOYMENT_TYPE.map do |type|
[type, t(type, scope: i18n_scope)]
end
end
end

Ruby on Rails Previous Searches Display when Typing in Search Bar

Ok so what I have is a library database system where a user can type in books in a search bar and then the books that match their search are displayed when they click enter. What I want to do is get it so that when the user types "a" into the search bar all records will be displayed in a drop down below the menu with the letter "a", and then when they input the next letter "l" all records with "al" will be displayed in a drop down, and then "ali", and so on. I do not have much knowledge of gems, but want to learn. Is their a way I can do this? I am using rails 4.0.1 and at the minute have a fuzzy search method.
Or if you want to roll your own...
This is the gist of it, might require a bit of tweaking:
routes.rb
get '/search' => 'search#autocomplete', as: :search_autocomplete
search_controller.rb
def autocomplete
search_term = params[:search]
#results = YourModel.where( "your_field LIKE search_term" )
respond_to do |format|
format.json #results
end
end
your_view.html.erb
<%= form_tag( search_autocomplete_path, method: "get" ) do %>
<%= text_field_tag( :search, params[:search], placeholder: 'Enter a search term...', :id => 'autocomplete_search' ) %>
<%= submit_tag( "Go" ) %>
<div id="autocomplete_search_results">
<% end %>
some_coffeescript_file.js.coffee
$( document ).ready ->
$.ajax '/search',
type: 'GET'
parameters: $( "#autocomplete_search" ).val()
dataType: 'json'
success: ( data ) ->
# here you'll have to append the results to whichever div/container you have in place
$( '#autocomplete_search_results' ).append "#{ data }"
Checkout Twitter typeahead.js jquery plugin https://github.com/yourabi/twitter-typeahead-rails.

pre-populating form field from database with "second level" association

I have three models: Appointment, Client, and InsuranceProvider
A client has_many :appointments
And a client has_many :insurance_providers (the idea being I"d like to store historical info there).
in my view to create a new appointment, I have this (among other things):
<%= f.association :client, label_method: lambda { |c| "#{c.first_name} #{c.last_name}" }, collection: current_user.clients %>
this is fine, but I'd like to get to the copay field in insurance_providers.
Basically, this is how you'd get there:
appointment.client.insurance_provider.copay
What I'd like to do is pre-populate the "copay amount" field based on the client selected from the dropdown.
How can I do this?
Please let me know if you need to see my models or views explicitly.
If I understand correctly, you want a second select to be populated with values based on the value in the association.
Basically, you need JQuery/AJAX to do this for you. JQuery to watch the first select, and then AJAX to get data from rails based on the value chosen, and JQuery again to add values to the second select.
An alternative would be to use an in-place editor like best_in_place for each select, which would do the AJAX-y stuff for you.
Use ajax to to fetch the values for copay based on the return of the select.
Because there are a lot of steps, I'll lay them out, but you can find them in probably a dozen other SO questions.
Add the Javascript, this coffeescript but it's just your basic on change -> send-data call - so change at will.
#appointment.js.coffee
$(document).ready ->
$(".client_select").on "change", ->
$.ajax
url: "/appointments/new"
type: "GET"
dataType: "script"
data:
client: $(".client_select").val()
Make sure your form has the 2 jquery elements to get data from and push data to.
# First the field to pull from
<%= f.association :client, label_method: lambda { |c| "#{c.first_name} #{c.last_name}" }, collection: current_user.clients, input_html: { class: 'client_select' } %>
# And then the field to push to
<%= f.input :copay_amount, input_html: { class: 'copay_from_client' } %>
This is going to make a request on your "new" action of your appointments controller, so you'll need to add a javascript respond to to make sure it can render the next step, the UJS file.
# appointments_controller.rb
def new
# ... All the stuff you're normally doing and additionally:
#you'll have to adjust the params argument to match your select field
insurance_copay = Client.find(params[:client]).insurance_provider.copay
respond_to do |format|
format.html # new.html.erb
format.js { render "new", locals:{insurance_copay: insurance_copay} }
format.json { render json: #appointment }
end
end
Now add the UJS, new.js.erb
$(".copay_from_client").val('<%= #insurance_copay %>');

Show ajax based elements after form submit error

I have two select elements in my form, Category and Sub-category. At first, only the category select is shown. When the user makes a selection in the category select, an AJAX request is sent to the server and the subcategory select is shown. It works fine.
Now, when there is some error in the form submit, due to anything, missing some value that is required, or anything, I show the error message, but I cannot retain the same state of the select boxes, I see only the category select, with no value selected, i.e the initial state. Can anyone suggest me how I can preserve the state of these select elements?
Here's a code snippet from my new form:
<div id="category-select">
category <%= collection_select :post_category, :id, #categories, :id, :name,
options = {:prompt => "Select a category"} %>
</div>
<div id="sub-category-select">
</div>
Here's my jQuery script that sends AJAX request when a selection is made on Category select:
$("#post_category_id").change(function() {
var category_id = $('select#post_category_id :selected').val();
if(category_id == "") category_id="0";
$.get('/posts/update_sub_cat/' + category_id, function(data){
$("#sub-category-select").html(data);
})
return false;
});
The AJAX request is made on the update_sub_cat action in the post_controller, which is shown below:
def update_sub_cat
if params[:id].present?
#sub_categories = Category.find_all_by_parent_id(params[:id])
else
#sub_categories = []
end
respond_to do |format|
format.js
end
end
The AJAX request renders update_sub_cat.js.erb file, in which I have used some HMTL
sub-category <%= collection_select :post_sub_category, :id, #sub_categories, :id, :name,
options = {:prompt => "Select a sub-category"} %>
I know, I should not directly use HTML here, but rather use $('sub-category-select).append(...), but I got it working like this, and am planning to change it later.
This is all the code that is involved in this part of my program.
Can anyone help me, please?
I solved the problem, and got the AJAX based element to maintain state. Here's my jQuery code:
$('#before_category_select').loadPage();
jQuery.fn.loadPage = function(){
if($("#new-post-area").length > 0){
$.getJSON('/home/cat_select_state.json', function(data){
$.get('/posts/update_sub_cat/' + data.cat_state, function(data1){
$("#sub-category-select").html(data1);
})
});
}
}
The controller action that I call to get the state of the select elements, which is stored as session variables at the time of page submit:
def cat_select_state
#cat_session = {:cat_state => session[:new_post_category], :sub_cat_state => session[:new_post_sub_category]}
respond_to do |format|
format.json {render :json => #cat_session}
end
end
And finally, I used a default values for the select boxes, which are stored as session variables. If the session variable is null, the default value is the prompt message for the select box.
<%= collection_select :post_category, :id, #categories, :id, :name,
options = {:prompt => "Select a category", :selected => session[:new_post_category]} %>
The HTML for sub-category select element is rendered in the javascript file update_sub_cat.js.erb.
sub-category <%= collection_select :post_sub_category, :id, #sub_categories, :id, :name,
options = {:prompt => "Select a sub-category"} %>
Please suggest if you have any more improvements.

RoR live-search (text_field_with_auto_complete) submit

I have a "Movies" and a "Actors" table and "Casts" as join-model. To be more specific "Casts" has movie_id, actor_id and rolename.
I want in "Movies" form to add a live search to search through actors and a "rolename" text_field and save those to "Casts".
I don't know if text_field_with_auto_complete is the right choice but i prefer not to use much javascript because i am not familiar with it.
I've been searching all over the internet to find something similar to this without any result.
I've manage to get it working with "#actors.each do" but it makes a very long list.
It's not a plugin, but with a little jQuery magic, you can make use of http://github.com/chadisfaction/jQuery-Tokenizing-Autocomplete-Plugin. The nice thing about this is that since it is pure JS in its implementation, you can create the AJAX call yourself in Rails and only display what you want. It even allows you to add a stylesheet to the dropdown if you want to make it more Facebook like. In your controller, add a function so the AJAX call will return a list of rows in JSON:
def taglist
tags = []
sql = "SELECT id,name ... LIMIT 15" # Enter SQL here to produce a possible result set
result = ActiveRecord::Base.connection.execute(sql)
# Iterate over the hash values and push them into an array
result.each { |field| tags.push( {"id" => field[0], "name" => field[1]} ) }
result.free
render :json => tags, :layout => false
end
In the view, add the following code:
<%= javascript_include_tag 'jquery.tokeninput' %>
<%= stylesheet_link_tag 'token-input-facebook' %>
<script type="text/javascript">
jQuery(document).ready(function () {
jQuery("#actors_role").tokenInput("/actors/rolesearch", {
allowNewValues: false,
canCreate: false,
hintText: "Enter the actor's name or role they played",
});
});
</script>
See text_field_with_auto_complete inside form_for
In the auto_complete controller action, make sure your SQL query is restricting the actors names using the passed param.

Resources