Use a controller variable in javascript (Ruby on Rails) - javascript

I want to use my controller variable in my javascript.
Since this cannot be done straightforward. I use gon gem. https://github.com/gazay/gon
With this what I do is in a before filter of my base controller (Which acts as a before filter for all the controllers) I do gon.variable_name = value And in my js file I use gon.variable_name. This works fine for full page reloads.
But the variable is not getting updated for ajax request.
Say for example:
On page reload, in my controller I do
gon.variable_name = value1
and in my js, gon.variable_name gives me value1.
After a ajax request in my controller I do
gon.variable_name = value2
and in my js, I still see gon.variable_name as value1 only.
Is there any way I could update gon.variable_name in a ajax request?
Thanks

You can use the watch: true switch for gon:
#app/views/layouts/application.html.erb
<head>
<%= include_gon(watch: true) %>
#app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
def update_vars
#value1 = User.count
gon.watch.value1 = #users_count
end
end
This is what the documentation says. I don't agree with it, but should work.
It looks like ajax polling which is notoriously inefficient.
--
To give you some context, you have to remember that gon basically creates a small hash of javascript in your layout, populating it with the gon variables you've assigned in your controller:
<script>
gon = {
value1: "test",
value2: "test"
}
</script>
We've used it before here:
The reason this is important is because this gon JS variable is invoked at runtime -- meaning that it remains static until you reload the page.
The way around this is to either dynamically reload the gon variable values (using watch above) or pass the new value through your ajax request (like Sergio recommended in the comments):
#app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
def update
respond_to do |format|
format.json { render json: {value_2: "test"}.to_json %>
end
end
end
You'd then be able to use it as follows:
$.ajax({
url: "/update",
success: function(data) {
var value2 = data.value2;
}
});

I have to initialize gon for every request inorder to update the variables.
include_gon(:init => true)
in the response of every request solved the problem.
Extra tip:
For those who use ajaxify rails gem you can call this helper method in the ajaxify_extra_content method and in the content_inserted callback you can append this to the head tag to get it work in a generic way.
Thanks all for your suggestions and help.

Related

Rails & AJAX, is there a reason you shouldn't render html view directly in controller action for ajax to process?

The classic way to work with Rails & Ajax is always something that looks like this:
// JS - let's assume this submits to dummies#create
$(form).submit()
# Dummies Controller
def create
#dummy = Dummy.new(dummy_params)
respond_to do |format|
format.js
end
end
# /views/dummies/create.js.erb
$("page").append("<%= escape_javascript(render partial: 'dummy_view' ) %>");
# /views/dummies/_dummy_view.html
<h1><%= #dummy.name %></h1>
I've always been curious, because the above seems to create a random create.js.erb file with very little meat... is there a reason (e.g., it's terrible convention, or terribly insecure or whatever), why you should NOT instead just render the view directly back to ajax?
// JS - basically takes responsibilites of create.js and puts it into the always
$.ajax(...).always(function(xhr, status){
$("page").append($(xhr['responseText']))
// responseText contains the partial rendered by the controller action
})
# Dummies Controller
def create
#dummy = Dummy.new(dummy_params)
render partial: 'dummy_view'
end
# /views/dummies/_dummy_view.html
# unchanged
<h1><%= #dummy.name %></h1>
NOTE above is pseudo-code, apologies for minor errors. The conceptual idea & question remain unchanged, though.
The create.js.erb is not random, is the view for the action with the expected format.
Generally, you may not have a view so simple (You may have different selectors other than "page", you may have some extra js code to be executed after or before the append), a js view/script is the general solution for an ajax request to give the response full control over what to do.
You could have what you want, but it will just work for your particular case when the selector is always "page" and you only want to append html to that element. Nothing prevents you from doing that (though you might want to use a custom ajax request and not rails' one since it sets js format by default and executes the response's script).
The convention is that a rails' remote request renders a js script, you can move out of the convention if you want. You'll lose a lot of flexibility with your approach as is (like... what if the create action fails an you need to display errors?).

Rails: Passing controller variable to JS using ERB slows page?

There are two ways I know of to pass variables defined in a controller (/action) to JS...
The official way is
.js.erb:
var banana = "<% #banana %>"
Another way (that I'm currently using is)
.html.erb
<span id="banana-variable" style="display:none"><% #banana %></span>
.js
var banana = $("#banana-variable").html()
This js file is loaded on multiple actions/views across the controller. It makes sense to me to not use a .erb extension: users cache it the first time they hit any action/view in the controller. They then won't have to download different versions of the file when they browse to different pages. Am I right?
Yes, are right. The javascript will be cached on the client's browser.
Still you want to send data on the 'js' or script files you can use this gem called Gon.
http://railscasts.com/episodes/324-passing-data-to-javascript
I recommend you use gem 'gon', which is thoroughly introduced in RailsCast. It makes your controller cleaner. Your method will make it more troublesome if you're trying to pass an array or hash to js.
I think your issue is that you are using <% %> which will execute the code, instead of <%= %> which will execute the code and render the result back into the template.
With Gon you can access the page only after the html page is loaded. From what I understand it uses web page as a proxy to transfer data from rails server to javascript.
But if you want to use the variable at any point of time you want, you can do an ajax(post) request from javascript to the rails controller and retrieve the value as json.
Example:
AJAX Request:
$.ajax({
type: "POST",
url: <PageURL>,
async: false,
dataType: 'application/json',
success: function(data){
data = JSON.parse(data);
},
});
your "data" will contain the returned value.
On the controller side it should be:
Controller:
def <PageURL>
render :json => {:result => <value_you_want_to_return>}
end
Do not forget to define the controller's path in routes.rb file.

Gon gem for ruby on rails watch function watch all variables

gon is a gem for ruby on rails to pass data from controller to view's javascript.
It works fine,just pass data,and watch one variable.
but when I have normal data(no need dynamic watch ) and data1 (need watch )teogeher,problem happens.
Gon watch all variables instead of only watch the gon.watch.data1.
That cause refresh time very very long (7k ms plus)
web page always hang.
anyone face this problem before? how to solve this?
code in my controller
class GmapsController < ApplicationController
def index
#i=0
#Nstatic=Array.new(NoiseDevice.all.length) {Array.new(4)}
#Vstatic=Array.new(VibrationDevice.all.length) {Array.new(4)}
#vabrdevices = VibrationDevice.find(:all)
#vabrdevices.each do |vabrdevice|
#Vstatic[#i][0]=Project.where(id: vabrdevice.project_id).pluck(:client_name).first.to_s
#Vstatic[#i][1]=vabrdevice.latitude #
#Vstatic[#i][2]=vabrdevice.longitude #
#Vstatic[#i][3]=VibrationDevice.where(id: vabrdevice.id).pluck(:cont_value).first.to_s
#i+=1
end
#i=0
#noiseDevices = NoiseDevice.find(:all)
#noiseDevices.each do |noiseDevice|
#Nstatic[#i][0]=Project.where(id: noiseDevice.project_id).pluck(:client_name).first.to_s
address=Project.where(id: noiseDevice.project_id).pluck(:jobsite_location).first.gsub(/\s+/, "+")
#Nstatic[#i][1]=noiseDevice.latitude #
#Nstatic[#i][2]=noiseDevice.longitude #
#Nstatic[#i][3]=NoiseDatum.order(:updated_at).where(noise_device_id: noiseDevice.id).pluck(:leq).first.to_s
#i+=1
end
gon.nstatic=#Nstatic
gon.vstatic=#Vstatic
end
def ndataupdate
#test=Array.new
#test=VibrationDevice.pluck(:cont_value).to_s
gon.watch.test1=#test
end
end
Instead of update my test1 only, it updates all data which is loaded from database in this controller. even if I delete
gon.nstatic=#Nstatic
gon.vstatic=#Vstatic
gon will load all data retrieved from database in this controller instead of part of it.
In order to make it display only refresh part , I delete all retrieve data function ...
maybe it's not answer, but maybe it somehow helps (comments are too long),
so, you could specify manualy url for updating variable like:
gon.watch('test1', {interval: <ms>, url: '<url>'}, '<callback>')
for checking available variables in your ndataupdate action you could write:
gon.all_variables
also you could try cleaning variables with:
gon.clear

How to retrieve a value from an AJAX call with Rails?

I have this scheme:
JS:
jQuery.validator.addMethod(
"check_membership",
function() {
console.log('A');
$.ajax({
type: "POST",
url: "/subscriptions/check_validity",
dataType:"JSON",
data: {coupon: "12345"},
});
console.log('<%= #aaa %>');
},
"mmmmm"
);
and Rails action:
def check_validity
#aaa = 'xxx'
respond_to do |format|
format.js { render :text => #aaa }
end
end
My goal is to display in Javascript the value from Rails - #aaa, but the output of
console.log('<%= #aaa %>');
is just an empty space...
But I am not sure what the proper workflow should be here (I need to pass just a kind of information like "yes"/"no").
Thanks guys!
Ajax sends a params variable of your data to the controller, just like a form for a new record. In your example, to retrieve the coupon value, you pass params the key you established in your ajax call:
def check_validity
value = params[:coupon] # returns "12345"
...
end
As noted in another answer, this will not yield the #aaa value in your console.log, or anywhere in your javascript. These variables are interpreted on the server side, but javascript only works on the client side (unless you're using server side javascript, but that's a horse of a different color). This can be confusing because we use Ruby variables in Views, but Views are interpreted on the server, before they are sent to the client. Javascript is not.
Once you pass a variable to Rails, through the params value, you'll need to send any new information back to the client, which is looks like you're trying to do with :text => #aaa. Many ways to tackle this issue, but you could start with the Railscast:
http://railscasts.com/episodes/324-passing-data-to-javascript
and also
http://railscasts.com/episodes/136-jquery-ajax-revised
Hm, a lot of confusion going on here:
console.log('<%= #aaa %>');
Is mixing server side and client side stuff. If you
render :text => #aaa
in rails, you don't get a variable named #aaa in your browser.
I'd recommend rendering json to the browser anyway...

Rails - Multiple remote forms hitting same controller action?

I'm working on an application in which a certain model is updated from a number of different locations using remote forms. I'm looking for a pattern to dynamically call the right JS callback after updating this model.
With a normal form this can be solved by passing a redirect url in the form itself and then redirecting to this url in the controller. The remote form side is harder: Should I pass a file name? Should I pass an arbitrary string and then switch on that string in a single .js.erb file? Any other suggestions?
Is this just a sign that the application should be restructured to prevent updating the same model from more than one location?
No it's fine If you can call the same controller action from different locations.
Your options:
1) Preferably this controller action can give the same response and will work for the different locations, ex. it just updates a container with a id which is present in all those locations.
2) You noted that redirects made things easy in the past, consider adding the following to your application controller:
def js_redirect_to(path, flash_messages = {})
flash_messages.each { |type, message| flash[type] = message }
respond_to do |format|
format.js { render :js => "window.top.location='#{path}';" }
end
end
This is the same signature as the normal redirect_to, it just allows you to redirect from a js request. Note that if you use turbolinks the js should be 'Turbolinks.visit(url);'.
3) If you really can't handle it generically like the options above, you could pass your JS namespace of the location you are submitting from in the form, and the controller calls the same method for all locations, it's just a different namespace. Ex:
Let say one location is from Pet administration, then in assets pet.js:
var pet = {
load = function() {
your page load js...
},
... more functions...
post_callback = function(html_segment1, html_segment2) {
this is where you handle the form callback for pets...
$('some_element').html(html_segment1);
$('another_element').html(html_segment2);
}
}
Construct more like these for other locations of your app. Using JS namespaces like this is anyway a good idea. Then your form submits a parameter :location => :pet to the controller, which responds with:
... your JS code that all pages should execute...
html_segment1 = "<%= escape_javascript(render 'some_partial') %>";
html_segment2 = "<%= escape_javascript(render 'other_partial') %>";
<%= #location %>.post_callback(html_segment1, html_segment2);
4) Use a widget gem, most popular is apotomo or cells.
5) Just use a case in the controller to render different views.
Hope this helps, let me know if you need clarification.

Categories

Resources