Dropping Receiving ID in Rails - ruby-on-rails

I currently have a rails webpage that uses the jRails and the jQuery library to run some ajax functions.
So far everything is working, including the delayedObserver plugin, which suggests that jRails is actually being put to use. However my 'drop_receiving_element' doesn't seem to work. This the Rails code:
<%= drop_receiving_element drop_id,
:onDrop => "function(drag_element, drop_element, event){
if (confirm(\"#{escape_javascript(_('This will remove User from this Group, are you sure?'))}\"))
{#{remote_function(:update => 'module_content',
:url => {:controller => :projects,
:action => :member_delete,
:id => #project.id},
:with => "'u=' + encodeURIComponent(drag_element.id)"
)};}
}",
:accept => 'RolesUsersSelection',
:hoverclass => "#{drop_class}_active"
%>
Which results in the following Javascript Code
<script type="text/javascript">
//<![CDATA[
jQuery('#RemoveThisMember').droppable({accept:'RolesUsersSelection', drop:function(drag_element, drop_element, event){
if (confirm("This will remove User from this Group, are you sure?"))
{jQuery.ajax({data:'u=' + encodeURIComponent(drag_element.id), success:function(request){jQuery('#module_content').html(request);}, type:'post', url:'/of/projects/11/member_delete'});}
}, hoverClass:'ProjectRoleDropDelete_active'})
//]]>
</script>
Yet I'm stumped as to why this isn't working.
This is the function called in the jRails.rb
def drop_receiving_element_js(element_id, options = {})
#convert similar options
options[:hoverClass] = options.delete(:hoverclass) if options[:hoverclass]
options[:drop] = options.delete(:onDrop) if options[:onDrop]
if options[:drop] || options[:url]
options[:with] ||= "'id=' + encodeURIComponent(#{JQUERY_VAR}(ui.draggable).attr('id'))"
options[:drop] ||= "function(ev, ui){" + remote_function(options) + "}"
end
options.delete_if { |key, value| PrototypeHelper::AJAX_OPTIONS.include?(key) }
options[:accept] = array_or_string_for_javascript(options[:accept]) if options[:accept]
[:activeClass, :hoverClass, :tolerance].each do |option|
options[option] = "'#{options[option]}'" if options[option]
end
%(#{JQUERY_VAR}('#{jquery_id(element_id)}').droppable(#{options_for_javascript(options)});)
end
Any ideas?
Thanks,

Turns out because I've switched over to jQuery, the parameters for
function(drag_element, drop_element, event)
were incorrect. Those are the correct parameters for prototype. They should read
function (ui,event)
if your using the jQuery library.

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

What is the Rails convention for sending form data to a URL dependent on form value?

I would like a form submitted at the url
/index/fruit
to submit the form data to
/index/:identifier
where :identifier is determined by a value of the form
What is the rails convention in this instance?
Is there a way to achieve this without a controller level redirect or javascript-updating the submit URL?
routes.rb
match 'smasher(/:action(/:id))', :controller => "customcontroller", :as => :smasher, :defaults => { :action => :index, :id => :fruit }
index.html.erb
<%= semantic_form_for :d, :url => smasher_path, :html => { :method => :get } do |f| %>
... form data ...
<%= f.input :identifier, :as => :hidden %>
<% end %>
My current implementation is similar to this answer
There's isn't really a "convention" for this, but rather one of those things where there's more than one way to do it.
One way that you could do it is still send the form to one and only one action within the controller, but then delegate in the controller which action to go to, like this:
def smasher
if params[:identifier] == 'this'
smash_this!
else
smash_that!
end
end
def smash_this!
# code goes here
end
def smash_that!
# code goes here
end
Heres the javascript version (though technically its all on an erb html template), if you're feeling up to it.
<%= f.input :identifier, :as => :hidden, :onchange => "$(this).setAction()" %>
<script>
// While you can this script block here within your erb template
// but best practice says you should have it included somehow within `<head></head>`
$(function() {
//create a method on the Jquery Object to adjust the action of the form
$.fn.setAction = function() {
var form = $(this).parents('form').first();
var action = form.attr('action')
form.attr('action', action.substr( 0, action.lastIndexOf('/')+1 ) + $(this).val());
}
});
</script>
Heres the pure javascript version:
$(function() {
//create a method on the Jquery Object to adjust the action of the form
$.fn.setAction = function() {
var form = $(this).parents('form').first();
var action = form.attr('action')
form.attr('action', action.substr( 0, action.lastIndexOf('/')+1 ) + $(this).val());
}
//we gotta bind the onchange here
$('input[name="identifier"]').change($.fn.setAction);
});

How to use jquery-Tokeninput and Acts-as-taggable-on

This is how you use autocomplete with jQuery Tokeninput and ActsAsTaggableOn.
In my situation i am using a nested form but it shouldnt matter. Everything below is code that works.
Code
Product Model:
attr_accessible :tag_list # i am using the regular :tag_list
acts_as_taggable_on :tags # Tagging products
Products Controller:
#1. Define the tags path
#2. Searches ActsAsTaggable::Tag Model look for :name in the created table.
#3. it finds the tags.json path and whats on my form.
#4. it is detecting the attribute which is :name for your tags.
def tags
#tags = ActsAsTaggableOn::Tag.where("tags.name LIKE ?", "%#{params[:q]}%")
respond_to do |format|
format.json { render :json => #tags.map{|t| {:id => t.name, :name => t.name }}}
end
end
Routes:
# It has to find the tags.json or in my case /products/tags.json
get "products/tags" => "products#tags", :as => :tags
Application.js:
$(function() {
$("#product_tags").tokenInput("/products/tags.json", {
prePopulate: $("#product_tags").data("pre"),
preventDuplicates: true,
noResultsText: "No results, needs to be created.",
animateDropdown: false
});
});
Form:
<%= p.text_field :tag_list,
:id => "product_tags",
"data-pre" => #product.tags.map(&:attributes).to_json %>
Issue 1(SOLVED)
Must have the line:
format.json { render :json => #tags.collect{|t| {:id => t.name, :name => t.name }}}
Note - You can use #tags.map here as well and you dont have to change the form either.
Below are the 2 issues on why you needed to do this:
I have the following Tag: {"id":1,"name":"Food"}. When I save a Product, tagged "Food", it should save as ID: 1 when it searches and finds the name "Food". Currently, it saves a new Tag with a new ID that references the "Food" ID, i.e. {"id":19,"name":"1"}. Instead, it should be finding the ID, showing the name, and doing a find_or_create_by so it doesn't create a new Tag.
Issue 2(SOLVED)
When I go to products/show to see the tags by doing <%= #product.tag_list %>. The name appears as "Tags: 1", when it really should be "Tags: Food".
How can I fix these issues?
You should define a route in your routes.rb which should handle products/tags path. You can define it like:
get "products/tags" => "products#tags", :as => :tags
Thus should give you a tags_path helper which should evaluate to /products/tags. This should get rid of the errors you mentioned in the question. Be sure to add this route before defining resources :product in your routes.rb
Now onto acts-as-taggable-on, I haven't used this gem, but you should look at method all_tag_counts documentation. Your ProductsController#tags method will need some changes on the following lines. I am not sure if its exactly what would be required, as I use Mongoid and can't test it out.
def tags
#tags = Product.all_tag_counts.(:conditions => ["#{ActsAsTaggableOn::Tag.table_name}.name LIKE ?", "%#{params[:q]}%"])
respond_to do |format|
format.json { render :json => #tags.collect{|t| {:id => t.name, :name => t.name } }
end
end
little add-on:
If you want to create the tags on the fly, you could do this in your controller:
def tags
query = params[:q]
if query[-1,1] == " "
query = query.gsub(" ", "")
Tag.find_or_create_by_name(query)
end
#Do the search in memory for better performance
#tags = ActsAsTaggableOn::Tag.all
#tags = #tags.select { |v| v.name =~ /#{query}/i }
respond_to do |format|
format.json{ render :json => #tags.map(&:attributes) }
end
end
This will create the tag, whenever the space bar is hit.
You could then add this search setting in the jquery script:
noResultsText: 'No result, hit space to create a new tag',
It's a little dirty but it works for me.
There is a bug in Application.js code. There is an extra ) after "/products/tags.json". Remove the extra ). The code should be:
$("#product_tags").tokenInput("/products/tags.json", {
prePopulate: $("#product_tags").data("pre"),
preventDuplicates: true,
noResultsText: "No results, needs to be created.",
animateDropdown: false
});
I don't know if this is the entirety of your error, but you are not hitting the proper URL with the tokenInput plugin.
This
$("#product_tag_list").tokenInput("/products/tags.json"), {
should be
$("#product_tag_list").tokenInput("/products.json"), {
As I said, I don't know if this is the only problem you are having, but if you change this, does it work?
EDIT:
I have never used ActsAsTaggableOn. Does it create a Tag model for you to use?
From the looks of it on github, if you wanted to query all tags, you might have to use its namespace as opposed to just Tag, meaning ActsAsTaggableOn::Tag. For example, you can see how they access Tags directly in some of the specs.
I had problems with editing the tags if for example the model failed to validate,
I changed
<%= p.text_field :tag_list,
:id => "product_tags",
"data-pre" => #product.tags.map(&:attributes).to_json %>
to
<%= p.text_field :tag_list,
:id => "product_tags",
"data-pre" => #product.tag_list.map {|tag| {:id => tag, :name => tag } }.to_json %>
If the form failed to validate on first submission, it was creating tags as the ID's of the tags it had created on subsequent submissions.
Two notes: if you're getting the tags changed by numbers on the POST request, use:
tokenValue: "name"
And if you're trying to add non-existent tags, use (undocumented):
allowFreeTagging: true

using both :url and :function on observe_form

Is there a way to generate both a Javascript function call and an Ajax call in the same observe_form tag? For example, something like this:
<%= observe_form 'user_filter_form', :url => { :action => :process }, :function => :fix_fields %>
Thanks!!
Your best bet here is to dig into the actual JavaScript instead of relying on the helpers. The helpers can only get you so far. What you want is something along these lines:
<script type="text/javascript">
new Form.EventObserver('user_filter_form', function(element, value){
fix_fields();
new Ajax.Request("/YOUR_CONTROLLER/process");
}
</script>
However, if you really want to rely on the Rails helpers you can do something like:
<%= observe_form 'user_filter_form', :function => "fix_fields(); #{remote_function(:url => { :action => :process })}" %>
Use :before or :after options instead of :function, depending on whether you want your function called before of after the Ajax request.
See documentation of link_to_remote helper for common options that can be passed to all the Ajax helpers like observe_form

Problems passing special chars with observe_field

I am working on a rails project. Using the tag observe_field, I am taking text typed into a text area, processing it in a control, and displaying the result in a div (very similar to the preview in stack overflow). Everything works fine until I type certain special chars.
? => causes the variable not to be found in the params object
(pound) => causes an invalid authenticity error
% => stops the div from being updated
& => every thing after the & is no longer passed into the variable on the server.
Is there a way to solve this?
--- code sample ---
this is the view. ( 'postbody' is a text area)
<%= observe_field 'postbody',
:update => 'preview',
:url => {:controller => 'blog', :action => 'textile_to_html'},
:frequency => 0.5,
:with => 'postbody' -%>
this is the controller that is called
def textile_to_html
text = params['postbody']
if text == nil then
#textile_to_html = '<br/>never set'
else
r = RedCloth.new text
#textile_to_html = r.to_html
end
render :layout => false
end
and this is the javascript that is created:
new Form.Element.Observer('postbody', 0.5, function(element, value) {new Ajax.Updater('preview', '/blog/textile_to_html', {asynchronous:true, evalScripts:true, parameters:'postbody=' + value + '&authenticity_token=' + encodeURIComponent('22f7ee12eac9efd418caa0fe76ae9e862025ef97')})})
This is an escaping issue (as stated by others).
You'll want to change your observe_field :with statement to something like:
:with => "'postbody=' + encodeURIComponent(value)"
Then in your controller:
def textile_to_html
text = URI.unescape(params['postbody'])
...
Can you provide a code sample?
More likely than not you'll just need to escape your HTML entities using encodeuri or something like that.
What does the generated Javascript look like?
Sounds (at first glance) like it's not being escaped.

Resources