Weird JSON Javascript problem in Rails - javascript

I'm trying to get my JSON from my controller to my view. In my controller I am doing:
#nodes = Node.all
#json = #nodes.as_json(:only => [:ID, :Lat, :Lon])
In my view I have tried:
1) var stuff = <%= #json %>
2) var stuff = <%= #json.to_json %>
3) var stuff = <%= #json.to_json.to_json %>
and all of those give me an error. I usually get an "Unexpected Syntax Error &" or "Unexpected Syntax Error {"
I have also tried using jquery and using respond_to within the controller, but that doesn't seem to work either.
My thoughts are that getting json to the view shouldn't be a big issue and shouldn't require jQuery, and currently, my page source looks like:
var stuff = [{"node":{"ID":1301499692582,"Lat":42.3605063113369,"Lon":-71.0870862191138}},{"node":{"ID":1301499691515,"Lat":42.3605147089149,"Lon":-71.0870533282532}},{"node":{"ID":1301431075499,"Lat":42.3605456103,"Lon":-71.0875239075536}} etc
I dont understand the &quot symbols (maybe thats where the syntax error is coming from) but when I do render :json => #nodes.to_json, the page renders a normal json that is valid:
[{"node":{"ID":1301499692582,"Lat":42.3605063113369,"Lon":-71.0870862191138}},{"node":{"ID":1301499691515,"Lat":42.3605147089149,"Lon":-71.0870533282532}},{"node":{"ID":1301431075499,"Lat":42.3605456103,"Lon":-71.0875239075536}}
Note: I've also tried doing var stuff = '<%= #json.to_json %> but when I do var json = JSON.parse(stuff), it gives me an illegal token error.
Can someone please help me with this? Thanks so much!

This is Rails html-encoding your string as is default in Rails 3.
You need to mark your JSON as html_safe:
var stuff = <%= #json.to_s.html_safe %>
Note that .to_s is needed because as_json gives Hash instead of string. You could do this instead:
# in controller
#json = #nodes.to_json(:only => [:ID, :Lat, :Lon])
#and in view
var stuff = <%= #json.html_safe %>

I think you need to put quotes around it, then you can ask jquery to parse the string into JSON.

Related

Rails JSON not Working in JS Function

I'm trying to get a json response from a rails controller and insert it into a js function. I keep getting errors:
MY CONTROLLER
def live #users present or attendees view
#presentation = Presentation.find(params[:secret])
#countdown = #presentation.start - DateTime.now()
#IF A PERSON IS A PRESENTER
if #presentation.presenter.id == current_user.id
#presenter = true
require 'json'
require 'net/http'
require 'uri'
uri = URI.parse('https://api.screenleap.com/v2/screen-shares')
req = Net::HTTP::Post.new(uri.path, initheader = {'accountid' => 'APPID', 'authtoken' => 'MYTOKEN'}) #These are correct in actual code
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
res = http.request(req)
#screenShareData = JSON.parse(res.body)
#IF A PERSON IS AN ATTENDEE
else
#presenter = false
#screenShareData = false
end
end
IN MY VIEW
<script>
<script type="text/javascript" src="https://api.screenleap.com/js/screenleap.js"></script>
window.onload = function() {
var callbacks = {
nativeDownloadStarting: [onNativeDownloadStartCallback],
screenShareStarting: [onScreenShareStarting],
appConnectionFailed: [onAppConnectionFailed],
screenShareStartError: [onScreenShareStartError]
};
var screenShareData = <%= #screenShareData %>;
var presenterAppType = 'NATIVE';
screenleap.startSharing(presenterAppType, screenShareData, callbacks);
};
</script>
In my view I also tried:
var screenShareData = <%= #screenShareData.to_s.html_safe %>;
In either case I seem to get one of two javascript errors. Either "Uncaught SyntaxError: Unexpected token &" or "Uncaught SyntaxError: Unexpected token =>"
My feeling is that it's somehow not translating the JSON the way the function expects it. Any idea how I might fix the issue?
You can pass the rails JSON variable I got from the controller by modifying the variable in the view as below:
<%= #screenShareData.to_json.html_safe %>
After that, the screenleap.startSharing function accepted the data and worked. It should be noted that I also removed the "callbacks" variable, but I isolated the change in the "screenShareData" variable as the one that fixed the issue.
You can't use irb syntax in js code
var screenShareData = <%= #screenShareData %>;
There are a few options to achieve what you want:
respond with json in your controller action and request it from client via ajax request
render this data #screenShareData in any of html tags data-attribute and get it with jQuery or vanilla js methods
UPDATE: I was wrong about that and you can inject ruby code in js. Here is an answer to your question:
var screenShareData = JSON.parse("<%= j #screenShareData.to_json.html_safe %>")
Where j is an alias to escape_javascript.
There is also another way:
var screenShareData = <%= raw #screenShareData.to_json.html_safe %>
But I would not suggest that because:
It might not work in some cases. If for example #screenShareData
has special characters and such
#screenShareData might have some code inside that will be executed in client browser
Also I suggest you to move api.screenleap.com request logic from controller action to service object.
What are you going to do if api.screenleap.com is unavailable or responds with an error?
Net::HTTP::Post is a blocking request means that if api.screenleap.com takes 5 seconds to respond then your client browser render will hold for 5 seconds. I suggest you do it async unless its desired behavior
You have different data formats in the same action response. If #presentation.presenter.id == current_user.id is true then its an array or an object. If false then its boolean. That's not cool

Rendering a partial and escaping the html/javascript for a bookmarklet

I am trying to build a bookmarklet that makes a request to an action in localhost and displays a div with the html taken from the js response from localhost. I can make the request just fine, but when the js returns I have "undefined" in console. I tried debugging it using binding.pry and I saw that the content is not evaluating properly. This is how I tried evalauting it:
content = "<%= escape_javascript(render(:partial => 'events/bookmarklet').html_safe) %>";
In the binding.pry console I get the following response:
content = eval("<%= escape_javascript render(:partial => 'events/bookmarklet') %>");
SyntaxError: (eval):1: syntax error, unexpected '<'
<%= escape_javascript render(:partial => 'events/bookmarklet') %>
^
(eval):1: syntax error, unexpected ')', expecting end-of-input
<%= escape_javascript render(:partial => 'events/bookmarklet') %>
^
from (pry):12:in `eval'
I don't know why you try to eval something if I understand what you want to do correctly.
Use json and render_to_string if you want to pass html by javascript. So for your example:
# controller
def action_name
content =
render_to_string(
partial: 'events/bookmarklet',
formats: [:html], # always render html format, even if request wants json
layout: false # render only partial, without any layout
)
render json: { html: content }
end
You can access the content in javascript by data.html then.

How can I render via CoffeeScript in Ruby on Rails 4

I was trying to solve this problem for a while with no result.
I've wanted to pass variables and load a render into a div using CoffeeScript in rails 4.
(I'm using SpreeCommerce platform).
view:
<%= link_to taxonomy.name,root_path+'t/'+tid, {class: "uno", remote: true} %>
controller:
respond_to do |format|
format.html
format.js # menu.js.coffee.erb
end
menu.js.erb.coffee:
$('div#productos').html("<%= escape_javascript(render :partial => /shared/products) %>")
I'd like to load the page '_products.erb.html' and the partial processes the variables that I give it. As soon as I know, view and controller are ok, the problem is in menu.js.erb.coffee
Any help will be apreciated!
ADDITIONAL:
I've modified the extension to .js.coffee.erb. When I try to run the app, it shows me:
"undefined method `render' for #<#:0xa70317c>"
I tryied using <%= raw escape_javascript( render :partial =>... almost always "render" method give me problems.
NEW INFO:
I added gem 'coffee-script' to the Gemfile (then 'bundle install').
Now, when I click the link_to, it shows me into the HTML <%= escape_javascript(render :partial => /shared/products) %> as a text instead of loading the "partial"... any suggestion please?
I wrote a post about this after struggling through the same problem.
You need to:
Name it menu.js.coffee. Suffixing .erb causes it not to be evaluated as CoffeeScript.
Use raw to escape it.
I used these two on my website. Here's how it looks:
<%= raw render 'path/to/menu.js.coffee' %>
It still processes ERB within your CoffeeScript.
I would recommend changing it from menu.js.erb.coffee to menu.js.coffee.erb.
Rails will process the file extensions from right to left. Meaning right now, your file is treated first as coffeescript, then as ruby, and finally as javascript. It looks like you want to make the ruby substitutions first, then parse the coffeescript into javascript, so that would be menu.js.coffee.erb
First of all, you should change file name from menu.js.erb.coffee to menu.js.coffee.erb and you need configuration file as follow, which is a contribution by cervinka on coffee-rails issue #36
config/initializers/coffee_erb_handler.rb
ActionView::Template.register_template_handler 'coffee.erb', Coffee::Rails::TemplateHandler # without this there will be template not found error
class ActionView::PathResolver < ActionView::Resolver
EXTRACT_METHODS = %w{extract_handler_and_format_and_variant extract_handler_and_format} # name for rails 4.1 resp. 4.0
method_name = EXTRACT_METHODS.detect{|m| method_defined?(m) || private_method_defined?(m)}
raise 'unknown extract method name' if method_name.nil?
old_method_name = "old_#{method_name}"
alias_method old_method_name, method_name
define_method(method_name) do |path, default_formats|
self.send(old_method_name, path.gsub(/\.js\.coffee\.erb$/, '.js.coffee'), default_formats)
end
end

Rails: pass data to javascript [duplicate]

This question already has answers here:
How to pass Ruby variables to a JavaScript function in a Rails view?
(6 answers)
Ruby on Rails - Send JavaScript variable from controller to external Javascript asset file
(2 answers)
Closed 8 years ago.
i'm new in Rails and in a controller i have:
class PagesController < ApplicationController
def home
#temp = "Hello"
end
end
I have read that i must put the javascript code in application.js (tell me if true) and i have:
window.onload=function(){alert("<%= j #temp %>")}
Obviously that alert print the string "<%= j #temp %>"
How can i pass the variable #temp to the javascript so that the alert can print Hello?
Thanks
I wrote an article on how to pass Ruby objects to the client. Ryan Bates also has an excellent RailsCast on passing data to JS.
Add a div to your view that corresponds to your the PagesControlle#home action that will not be visible when you load the page but will contain the data stored in the Ruby objects:
# views/pages_controllers/home.html.erb
<%= content_tag :div, class: "temp_information", data: {temp: #temp} do %>
<% end %>
Load the page with this div included and view the page source. You can see your Ruby objects stored in the .temp_information div. Open up the JavaScript console to access the Ruby objects as JavaScript objects:
$('.temp_information').data('temp')
You do not need to add your JS to a JS partial, you can also use the asset pipeline.
I do something similar to, but simpler than gon. I have the following in my ApplicationController.
def javascript_variables(variables)
#javascript_variables ||= {}
#javascript_variables.merge!(variables)
end
Within a controller action I can then do something like
def some_action
javascript_variables(user: current_user)
end
In my ApplicationHelper I have something like this
def javascript_variables(variables = nil)
#javascript_variables ||= {}
#javascript_variables.merge!(variables) and return if !variables.nil?
output = ''
padding = #javascript_variables.keys.group_by(&:size).max.first
#javascript_variables.each do |variable, value|
output << "#{variable.to_s.ljust(padding)} = #{value.to_json},\n "
end
raw "var " + output.strip.html_safe.gsub(/\,\Z/m, ';')
end
and finally in my layout's <head> I have
<script>
<%= javascript_variables %>
</script>
This gives me something like this (from a real example in my application)
<script>
var pageModule = "site/index",
isCustomer = false,
utype = "normal",
isAnonymous = true,
keyboardShortcuts = false,
pubnub = null,
requestToken = "3zj974w074ftria3j";
</script>
Take a look at this.
http://tech.thereq.com/post/17243732577/rails-3-using-link-to-remote-true-with-jquery-ujs
One of the easiest ways is to use js.erb file, where you can do ruby tags to access variables that you defined in the controller action.
You need to use a respond_to block in the controller action, specifying the action to be able to respond to javascript.
items_controller.rb
class ItemsController < ApplicationController
def action
respond_to do |format|
format.js
#format.html {} # These are for allowing other types of formats to be responded to.
#format.json {} # But they are not necessary for using this js.erb way of doing things.
end
end
end
/views/items/action.js.erb
$(div).html('The cat has erased your page');

Spine JS error rendering .eco template using Hem : "Cannot read property 'length' of undefined"

Learning spine.js I completed both the tutorials no problem, seems like a great framework, but this simple little problem is driving me nuts, because I have no idea what I can do to fix it...
From what I understand the variable #list should be accessible by the .eco template (compiled by hem), but it's not, has anybody else encountered this?
Can someone please show me where I am going wrong?
Users.coffee
Spine = require('spine')
User = require('models/user')
$ = Spine.$
class Show extends Spine.Controller
className: 'UserApp'
events:
'click .NewUser' : 'new'
constructor: ->
super
User.bind('create refresh change', #render)
#active #render
render: =>
#get all users and render list
#list= [1,2,3,4,5]
console.log(#list)
#html require('views/UserApp')(#list)
new: ->
#navigate('/users','create')
UserApp.eco
<% if #list.length: %>
<% for i in #list: %>
<%= i %>
<% end %>
<% else: %>
Why you no work!?
<% end %>
#html require('views/UserApp')()
expects an hash object as parameter. So, if you want to use a #list variable in your view (ala Rails I mean) you have to do something like the following:
#html require('views/UserApp')(list: #list)
where the key will be the name of your variable in the view. So using:
#html require('views/UserApp')(#list)
like you're doing will bring to the view the #list variable as the current # or this and in your view you should be able to use it in the following way:
<% if #.length: %>
<% for i in #: %>
<%= i %>
<% end %>
<% else: %>
Why you no work!?
<% end %>
But it's not that readable.
I think the template expects to receive an object. Then you access a property of that object by using #key_name;
Try something like this ( Disclaimer: I don't know Coffeescript )
render: =>
#get all users and render list
#item = {}
#item.list = [1,2,3,4,5]
#html require('views/UserApp')(#item)

Categories

Resources