Rails 4: Passing variables to javascript - javascript

I've tried numerous methods and followed Ryan Bates' guide but no matter what I do I still get undefined.
application.html.erb
<body>
<%= content_tag :div, id: 'trackers', data: {trackers: User.count} do %>
<% end %>
</body
application.js.erb
var datadump = ($('#trackers').data('trackers'))
console.log(datadump)
//returns undefined in console
Viewing page source I can see the variable
<div data-trackers="2" id="trackers">
I'm passing User.count for now just to keep it simple but I'll need to be passing #trackers_count which is instantiated in a before_action in the application controller. I should be able to sort that out though once I figure out the problem here. Any suggestions?
UPDATE - I've simplified all variables down to just trackers, instead of trackers_count to prevent any errors from syntax and updated code here to reflect that.
ANSWER UPDATE - I selected the correct answer because if you want to pass any variables ASIDE FROM CURRENT_USER those methods worked perfectly. However, you can't access anything with current_user in JS because it loads before the window, so it can't know who the current_user is. It's a real pain but I just did it the long way and accessed the info I need through passing in json and an ajax requests.

I used to do:
var my_var = <%= User.count %>
console.log(my_var)
If it is an integer this works just fine.
If, however, you want to pass objects, then use:
var my_var = JSON.parse(('<%= (#Users.count) == 0 ? "[]" : Users.first(10).to_json %>')
console.log(JSON.stringify(my_var))

You forgot about document ready. Try:
$(function(){
var datadump = ($('#trackers').data('trackers'));
console.log(datadump)
});
Or for provide data from Rails to JS use Gon gem https://github.com/gazay/gon

Stumbled here from google. In my case, I was trying to pass an array of objects to a js.erb, which triggered an event a front end component listened to. For anyone from google, I solved this issue by doing the following:
In my controller.rb:
#payload = []
array.each do |a|
temp = {
foo: a.id,
bar: a.relation.relation
}
#payload << temp
end
in my js.erb:
$(document).trigger('event', { data: <%= #payload.to_json.html_safe %> })
in my component.js:
$(document).on('event', (e, data) => {
console.log(data) //should be array of objects in proper format
})
Hope this helps someone!

This could help you.
For me, I wanted to turn this array
#dates = ["2018-12-24", "2018-12-25", "2018-12-26", "2018-12-27", "2018-12-28"]
into a variable in Javascript. I tried the basic:
var dates = <%= #dates.join(", ") %>
alert(my_var)
This alerted this exact text:
1995
I have no clue why.
Since it wasn't working I tried Guru's solutions up there by copying and pasting, but it was worse now I had an error coming up in my code. Finally I got Guru's solution to work like this:
(the print embedded ruby open and close worked outside the JSON.parse() and there was a missing ")" )
var dates = <%= JSON.parse(#dates.join(", ").to_json)%>
alert(dates)
But even though it didn't crash, want to guess what it alerted?
Yeap, you guessed it:
1995
So I gave up and I used a work around:
In my view edit.html.erb
<div class="dates"><%= #dates.join(", ") %></div>
<script>
var dates = $(".dates").text()
alert(dates)
$(".datepicker").val(dates)
</script>
That allowed me to feed the variable, therefore alert the correct data and set the Datepicker's input with the correct information. I'm still bugged for not being able to solve the hole 1995 problem. If someone could be so kind as to explain, I would really appreciate it. Hope this helps and have a great day!

Related

Dojo request.post to Rails Fails

I'm converting a website from Symfony to Ruby on Rails and am finally down to my javascript pop up email contact form. I don't want to rewrite the form or the java script as it took a lot of work to get it to work in the first place. That will be phase two.
Here's where I'm having the problem:
sendBtn = new Button({
label: "Send",
onClick: function(){
if (emForm.validate() == true){
// Post the data to the server
request.post("/contact/create",
{data: domForm.toObject("contact-form"),
// Wait 2 seconds for a response
timeout: 2000
}).then(function(response){
emailDialog.hide();
alertDialog.set("content",response)
alertDialog.show();
});
}
}
},"submit-btn");
I know it gets to the request.post as I copied the line "email.Dialog.hide()" just before it and it hid it. I later added code to catch any errors. It goes there immediately and not after the two second timeout. I'm using dojo here, by the way. So I suspect it doesn't like the "/contact/create" as it's the only thing I changed.
In my routes.rb I have:
get 'contact/create'
Do I have the right information in my post? If not how do I get there? The javascript is included in application.html.erb so it can be invoke from all pages on the site.
In case it's pertinent, my contact_controller.rb is currently just this:
class ContactController < ApplicationController
def create
respond_to do |format|
#format.html {}
#format.js {}
format.json { render :json => {:response => 'Amazing, it works'} }
end
end
def show
end
end
Take a look at your network tab in dev tools, it should tell you why it is failing post... I'd say try adding handleAs: 'json' option to your request.post. For more on dojo/request, read this
As it turned out, I had the right path in the request.post statement. I found out my copying the Javascript into my html.erb file so I could use
<%= contact_create_path %>
in it's place. I ended up getting the same value so that wasn't the problem. I then checked my Firebug console. Rails sends a nice dump of the problem. I was getting a 404 error. The problem was that I was doing a post and there was no route for it. So I changed the routes.rb file from
get 'contact/create'
to
post 'contact/create'
This might cause me other problems later on if I want to do a non-Javascript version.
I then got another error:
ActionController::InvalidAuthenticityToken in ContactController#create
Through the help of Stackoverflow I found the fix. I added the second line below:
class ContactController < ApplicationController
skip_before_filter :verify_authenticity_token
...
Again, this solution may cause other problems. Skipping verification doesn't seem like a good thing. I had a number of other problems getting the whole process to work, but their specific to my application.

Rails 3 Passing a variable from link_to to javascript

If a user is on a form page, I am trying to throw a confirm message if they navigate away without clicking the update button.
The coffeescript code I am using is as follows
# used to detect if a user is navigating off a page
if form_click == true
window.onbeforeunload = ->
"You have unsaved data! do you really want to exit?"
If I use if 1 ==1 or 1 ==2 as a test case, it works perfectly fine. However, I am having diffculty in sending a variable from the link_to code to set form_click.
I have tried the following
<%= f.button :button, :class => 'btn-primary',
data: {form_click: true},
remote: true %>
But I am not able to pass the variable to the coffeescript code. I am definitely not proficient with javascript and coffeescript, as this probably shows, and would be grateful of any advice on how to resolve this would be much appreciated
Have a look at the generated HTML. I suspect that the link_to is creating something similar to:
<button class="btn-primary" data-form_click="true" data-remote="true"></button>
If so then the coffeescript you need to have would be something like:
$('data-form_click').click( ->
if $(this).attr('data-form_click') == 'true'
window.onbeforeunload = ->
confirm("You have unsaved data! Do you really want to exit?")
)
The code above is off the cuff, but the main point is that the attribute on the button is a string, not a boolean, so you need to adjust accordingly.
Use the JS debugging tools to place a breakpoint so you can inspect the value of form_click is also another way to see what you should be comparing to.

Include a Coffeescript file with ERB in a view

This is giving me a major headache...
So I have an app which requires a sidebar that lists various information to do with a user's player. One section of this sidebar is a friends list. Now, when Player A sends a friend request to Player B, the request should be automatically logged in B's sidebar, and I intend to use WebSockets to do this.
Here is my cp.js.coffe.erb file (there's only a few snippets of ERB at the moment; there will be loads more and I rather get this working first):
$ ->
$("#cp").accordion()
if `"WebSocket" in window`
socket = new WebSocket("ws://localhost:8080")
socket.onopen = =>
console.log("Connection Open")
init = {
sender: "cp"
action: "init"
user: <%= #user.id %>
token: <%= cookies["remember_token"] %>
}
socket.send(init.to_json)
socket.onerror = (e)=>
console.log(e)
socket.onclose = =>
console.log("Closed")
socket.onmessage = (m)=>
console.log("Recieved: #{m.data}")
msg = m.data.JSON.parse
switch msg.action
when "ret_init"
when "friend_udt"
refreshFriend()
refreshFriend() ->
html = "<%= j render 'layouts/friends' %>"
$('#friends').empty()
$('#friends').add(html)
Theoretically, the code itself works fine, the problem being that Rails doesn't let you use ERB in the assets pipeline, and so this file has to sit in app/views/layouts.the file cannot access the variables declared within a controller or use the render method (or most other ERB methods).
Here's the thing: I can't include said file in my application.html.erb file, and I looked into requesting the file with AJAX, but from my understanding that will immediately execute the Javascript once and once only, and I need the methods in this to be constantly available to update the sidebar.
Is there any way of including this file so that it works with the ERB and the CoffeScript so that it would be continuously avaliable to the page? Am I misunderstanding the whole AJAX requesting method?
Thanks #nzifnab for your help with the JS. Now my friends partial looks like this:
<ul id="friendlist">
<% if Relation.find_by(owner: #user.id, type: "freq") != nil %>
<% Relation.find_by(owner: #user.id, type: "freq").each do |r| %>
<li class="friend-request-cp"><%= link_to "/#{User.find(r.character).name}" %></li>
<% end %>
<% end %>
<% if Relation.find_by(owner: #user.id, type: "friend") != nil %>
<% Relation.find_by(owner: #user.id, type: "friend").each do |r| %>
<li class="friend-cp"><%= link_to "/#{User.find(r.character).name}" %></li>
<% end %>
<% end %>
</ul>
I need to apply two different styles to each item, hence why I'm using the ERB here. This works fine, as it's loaded when the page is first navigated to, but my code was supposed to re-render that partial every time a notification comes through of any new interactions. It would then repopulate the list using the data from the database again. Is there a more efficient way of doing this? Can I still do this with the hamlcoffeeassets gem you showed me?
Slight tangent ensues:
By the way, I'm using Ruby 2.0.0-p247 and Rails 4 on Windows 7. I felt the need to include that because of some major compatibility issues with gems that are much different from Ubuntu. I had to move from Ubuntu to Windows because updating from 13.04 to 13.10 broke everything Ruby Gem on that OS. I don't have tome to find a fix: I literally have only four days to get this app built.
You can kinda use erb in the asset pipeline, but you have to remember that it only gets rendered ONCE, EVER, and not once for every user and so even if there was an #user variable (which there won't be), it would never change. You can use erb in your coffee file for things like route paths and environment variables, but not for things like user-specific config and dynamic changes to the JS. It's bad practice anyway.
What you should really do is use a javascript library to read cookies instead of trying to do it with rails (This will give you access to some of the things you appear to be trying to do). And when you need more dynamic behavior you should render data-attributes or other values into the html DOM itself and use the javascript to read that.
Take a look at this cookie library: https://github.com/carhartl/jquery-cookie
There's many others to look at via a quick google search.
socket.onopen = =>
console.log("Connection Open")
init = {
sender: "cp"
action: "init"
user: $.cookie('user_id')
token: $.cookie('remember_token')
}
There are a couple of ways to render new markup for your view using JS. One way is to use js templates. I'm a big fan of the hamlcoffeeassets library here: https://github.com/netzpirat/haml_coffee_assets Although it uses haml for the view, and not ERB. There are ERB variants as well.
You would add some markup to app/assets/templates/friend.jst.hamlc like so:
%p This is my friend markup #{#friend.name}
And then you can render it from your JS like this:
$('#friends').append(JST['templates/friend'](friend: {name: 'Bob'}))
Which will append the markup from your template with the values you've passed interpolated in. In my example you'd end up with this markup inside your #friends container:
<p>This is my friend markup Bob</p>
Alternatively you can render the partial you want via rails into your JSON response as just a string, and then insert that into your document...
So your JS might look something like this:
socket.onmessage = (m)=>
console.log("Recieved: #{m.data}")
msg = m.data.JSON.parse
switch msg.action
when "ret_init"
when "friend_udt"
refreshFriend(msg.friendHTML)
refreshFriend(html) ->
$('#friends').html(html)
UPDATE
In reference to your ERB question... First of all your partial is incredibly inefficient making similar calls to the database four times every time you render it. haml_coffee_assets is for use with the haml markup language (which I prefer over ERB), if you want ERB then use eco instead: https://github.com/sstephenson/eco
If you want to render this in the JS, then you need to send this "friend relation" data as JSON through the notification data response, you do not have access to active record OR any controller methods or instance variables when rendering javascript partials - they don't hit back to the server, they only use what is accessible by your javascript at the time.
This should really go to app/assets/javascripts/cp.js.coffee.erb, you can use erb in the asset pipeline just fine (see here) Make sure you are spelling the coffee extension right, though!
Doing this, you should be able to call this via ajax without problems, the path would be /assets/cp.js.
try this gem: 'coffeebeans'
name your coffee file as "some_file.html.erb"
<%= coffeescript_tag_do %>
# your coffee script here ...
<% end %>
in another erb file:
<%= render file: '.../some_file' %>

How do I make a ruby variable accessible to a javascript file?

I have two variables, <%= #user.lat %> and <%= #user.lng %>
These variables change depending on the user whose logged into my system - it's the address the user gave when registering with my app.
In a scripts.js file I've been trying to define them, so my Google map can show with the user's latitude and longitude.
But
function initialize_google_maps() {
var currentlatlng = new google.maps.LatLng(<%= #user.lat %>, <%= #user.lng %>);
etc, etc doesn't work, because it can't understand my ruby code.
I tried defining them at the top of the scripts.js file like:
var map_latitude = "<%= #user.lat %>";
var map_longitude = "<%= #user.lng %>";
and then using:
function initialize_google_maps() {
var currentlatlng = new google.maps.LatLng(map_latitude, map_longitude);
but I've learnt that you just can't put ruby in a js file. I did try renaming it to scripts.js.erb but that didn't work either.
So, how can I define <%= #user.lat %> and <%= #user.lng %> so they'll be recognised by my scripts.js file, and show up in my maps? I did try this answer here, creating a partial, but it didn't work for me. Maybe I was doing it wrong.
Please note: I can't simply put the code and maps function between script tags in a html.erb file because I'm using some ajax, and things get messed up - the ruby variables need to be recognised by the js file. Thanks.
It's possible to use Ruby in JavaScript file, but it's not recommended so I will not explain how.
To answer your question, you can just put the variable in your HTML attribute:
<div id="foo" data-lat="<%= #user.lat %>">Books are coming soon!</div>
And then extract it with JavaScript:
var lat = $("#foo").data("lat");
http://railscasts.com/episodes/324-passing-data-to-javascript will get you going in the right direction.
I think unobtrusive javascript is probably a good way to do this:
http://railscasts.com/episodes/205-unobtrusive-javascript
As the others suggested, you should aim for unobtrusive javascript.
Yet, you might want to use embedded javascript code in a js response for a view, for example. In this cases, you should use the erb extension in your javascript files, so the correct is.js.erb.

How to make jQuery remember state in a Rails app?

I'd be very grateful for a code snippet showing off jQuery functionality remembering state, e.g. .slideToggle().
I've read about local storage, session storage, ajax among others - what technique is preferred 2012 and how could an implementation look like in Ruby on Rails 3.2?
This episode on RC gave me nearly all information on how to solve it: http://railscasts.com/episodes/136-jquery-ajax-revised
I created an ajaxed link with remote: true in the view. This link renders as well a dynamic class retrieved from a boolean value in the session.
<%= link_to "Click here", {action: 'retain_widget_state'}, id: "switcher", class: "#{session[:switch]}", remote: true %>
I added an empty action in the controller. And then in a retain_widget_state.js.erb i have the code below. In it I toggle the state in a session and then run jQuery code showing or hiding the element via a click on the link above.
<% session[:switch] = true if session[:switch] == nil %>
<% if session[:switch] == false %>
<% session[:switch] = true %>
$('.company_view').slideDown(400);
<% elsif session[:switch] == true %>
<% session[:switch] = false %>
$('.company_view').slideUp(400);
<% end %>
Wrapped in a $(document).ready(function() {}); I put the conditional code that "remembers" the state of the toggle upon a refresh of the page:
if ($('#switcher.true').length) {
$('.company_view').display();
}
else if ($('#switcher.false').length) {
$('.company_view').hide();
};
There's more than one way to do this. To store a variable in the url as a parameter (accessible from the params[] hash in rails), in your javascript you can write something like (example taken from this question )
$.ajax({
data:{"toggle":state}, # This stores the toggle variable in the
# url like so: http://localhost:3000/?toggle=1
# Other stuff });
If you want something a bit more durable, try a cookie. There is a cookies[] hash in Rails3 (not sure about rails2). The session[] hash is stored as a cookie by default as well. You can check out this railscast on making a "remember me" login function which used the cookies[] hash. Also see the docs on the Cookie class in Rails3, which gives a pretty good explanation of the various options and methods you have available.
With jQuery, you will apparently need a plugin as the functionality to read/write cookies with jQuery is not there automatically (I was surprised to find that). Here's one on github that seems to be recommened and has some good documentation, though I haven't used it: https://github.com/carhartl/jquery-cookie
If you want to be able to retrieve the state at anytime, I would create records for the cases you want to save. Name, value and possibly page. Then, you can retrieve those values on page creation and use those values instead of hard-coded values.
HTTP is stateless in itself, meaning you've got to store the data somewhere (jQuery won't "remember" anything). Depending on your intended use, the state of the toggle field could be stored in a backend database, or stashed in a cookie on the user's browser.

Categories

Resources