How to properly pass a JSON in rails - javascript

I am new to web development and rails as well. I created a web app for internal use in php and now am converting it to rails. Trying to find out what render does is difficult. For example I find definitions like this:
render(options = nil, extra_options = {}, &block) protected
Renders the content that will be returned to the browser as the response body.
It seems nobody told the author that you do not use a word in its definition.
I was trying to understand render because according to How to pass json response back to client that is a better way of doing the task than the approach I have tried. But without the other peices I do not know how to implement it.
Could be due to my lack of web experience so if anyone has any links to definitions thats may help please post them.
I get this error:
Error in GetData: JSON.parse: expected property name or '}' at line 2 column 3 of the JSON data
When I print the string in an alert box it appears as one long string so I do not know where "line 2" is. If I set the limit to 1 I get the same error which really makes "line 2" difficult to find.
Here is an example of the data I get back:
[{"DocumentNbr":"SS9230","DocumentRevision":""},{"DocumentNbr":"SS8640","DocumentRevision":"17"},{"DocumentNbr":"SS8618","DocumentRevision":"4"},{"DocumentNbr":"SS8630","DocumentRevision":"20"},
I don't know if the " is supposed to be spelled out as &quot or at least thats how it is displayed in the alert box. I do not know if thats normal or an error that is causing the JSON.parse to fail. Any other ways to check data besides the alert?
I have a javascript function to call 'GetData' in the view:
var wholeNumberData;
wholeNumberData = GetData('wholeNumber', wholeNumber);
Which looks like this (stripped down version):
function GetData(getType, param) {
var data;
var params;
params = 'wholeNumber=' + param;
data = SendRequest('wholenumber/index', params);
return data;
}
function SendRequest(source, params) {
var http = new XMLHttpRequest();
http.open("GET", source + '?' + params, false);
http.setRequestHeader("Content-type","application/json");
http.onload = function() {
//alert('in function');
}
http.send(params);
alert(http.responseText);//Works
return JSON.parse(http.responseText);//FAILS
}
The route wholenumber/index points to an index.html.erb cionatining this:
<% #list = Wholenumber.where("DocumentNbr LIKE ?", params[:wholeNumber] + "%").limit(10) %>
<%= #list.to_json(:only => [:DocumentNbr, :DocumentRevision]) %>

This is kind of an unusual way to do it, but you could just add html_safe to your to_json method and it should work how you have it.
<%= #list.to_json(:only => [:DocumentNbr, :DocumentRevision]).html_safe %>
If you want the control to render JSON, rather than trying to parse JSON from html, you can have the controller action do something like this:
def action
#list = Wholenumber.where("DocumentNbr LIKE ?", params[:wholeNumber] + "%").limit(10)
render json: #list.to_json(:only => [:DocumentNbr, :DocumentRevision])
end
The Rails Guides have a more thorough walkthrough of Rails rendering

Related

Render flask template by clicking dynamically generated list item and passing 1 parameter to the back end

Ok so I am working with 2 ideas here and running into roadblocks on both... need guidance on which one is best and how to make this work.
The general idea is that I am dynamically generating a string of HTML which adds a list item. the user can select a list object and this will pass data from that list item that will be sent to flask, worked on a little bit, then a template is rendered with parameters based on the data we received from the user input.
Let's look at an example of the two ideas I'm working off of and where I am running into trouble.
In both cases, we are dynamically creating a list based in JS based on some input we receive from the user.
The first idea is to add a button with an onclick event that passes the bar as the parameter.
var data = "<ul>";
var max= Object.keys(res.foo).length;
for(var i = 0; i< max; i++){
let bar= (res["bar"][i]);
let foo = res["foo"][i];
data += "<li>"+foo+" <button type='button' onclick = 'sendBar(this.id)' class='btn btn-primary btn-sm' id='"+bar+"'>></button></li>";
};
data += "</ul>";
$("#datalist").html(data);
I have a function with the goal of sending the data to the back end
function sendBar(Bar){
fetch("/foo-bar", {
method: "POST",
body: JSON.stringify({ Bar: Bar}),
success: function(response){
this.window.location.href = '/foo-bar'
}
});
}
With this data, the goal is to query the database and grab some data based on the value that is posted.
#views.route('/foo-bar', methods=['POST'])
def fooBar():
data= json.loads(request.data)
Bar= data['Bar']
sql = "SELECT * Foobar FROM foobar where bar ='"+Bar+ "'"
fooBar_data=db.engine.execute(sql)
df = pd.DataFrame(foobar_data, columns = ['foobar','foo','bar'])
return render_template("foobar.html", data = df.to_html(classes ='table table-striped'))
this is not working. I have seen in some places this is not a good use of ajax. Clearly I'm not really sure either way.
The other idea is instead of using a button/onclick event... just render the template using {{url_for('views.fooBar',Bar = Bar)}}
I think I am running into trouble figuring out how to put this into the string builder I am working with in JS... it's such a mess with quotes and I feel like I may need to escape the jinja call, but I don't know how. here is what I was playing with.
data += "<li>" + res["note"][i]+"</li>";
obviously this is not the right way to do this... maybe someone can help me formulate this string in the proper way so that I may try this approach and see if I can get my desired result?
Or maybe someone has a better idea. thanks !

Render template in Django view after AJAX post request

I have managed to send & receive my JSON object in my views.py with a POST request (AJAX), but am unable to return render(request, "pizza/confirmation.html"). I don't want to stay on the same page but rather have my server, do some backend logic on the database and then render a different template confirming that, but I don't see any other way other than AJAX to send across a (large) JSON object. Here is my view:
#login_required
def basket(request):
if request.method == "POST":
selection = json.dumps(request.POST)
print(f"Selection is", selection) # selection comes out OK
context = {"test": "TO DO"}
return render(request, "pizza/confirmation.html", context) # not working
I have tried checking for request.is_ajax() and also tried render_to_string of my html page, but it all looks like my mistake is elsewhere. Also I see in my terminal, that after my POST request, a GET request to my /basket url is called - don't understand why.
Here is my JavaScript snippet:
var jsonStr = JSON.stringify(obj); //obj contains my data
const r = new XMLHttpRequest();
r.open('POST', '/basket');
const data = new FormData();
data.append('selection', jsonStr);
r.onload = () => {
// don't really want any callback and it seems I can only use GET here anyway
};
r.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
r.send(data);
return false;
In your basket view function, you always render the template as response. You can pass the parameters to your js snippet via HttpResponse. After request completed and you have response in your js function, you can use window.location.href to redirect the page you want. You can also look this answer to get more information.

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

Embed raw data in HTML to parse in jQuery

I've been living in the desktop world for most of my career, so forgive me for asking such a basic question, but I'm not quite sure where to start looking.
I want to return some raw data along with my HTML, and parse and display the data using jQuery as soon as the HTML is ready. I know roughly what my js code should look like, but I'm not sure how I should embed the raw data in my HTML.
I could use $.getJSON(), but it'd be better if I could have the data right in my HTML.
I think either json or XML would work, but what's the proper way to escape/embed/parse these when they're embedded in HTML?
Thanks in advance.
You can put the JSON data in a hidden div, then decode and use from jQuery.
For example, we'll take:
{"foo":"apple","bar":"orange"}
Escape it and put in a div:
<div id="data">%7B%22foo%22%3A%22apple%22%2C%22bar%22%3A%22orange%22%7D</div>
Then from jQuery, we can use the data:
var data = jQuery.parseJSON(unescape($("#data").html()));
So calling alert(data.foo) will give us apple.
Here's a working example.
Where and when do you want this data?
If you want it in your view, just pass the data to the view
Action/Controller:
public ActionResult MyAction()
{
ViewData["MyData"] = "this is a sample data of type string";
return View();
}
And then, somewhere in your view:
<script>
var data = '<%= ViewData["MyData"] %>';
$(document).ready(){
alert(data);
}
</script>
<h1><%: ViewData["MyData"] %></h1>
Of course, if you're working with a List<string> or `string[]', you would need to format it to proper JavaScript for jQuery to understand it.
<script>
var dataArray = [
<% foreach(string s in (string[])ViewData["MyDataArray"]){ %>
<%= s %>,
<% } %>
];
</script>
It would be getter if you generated the proper JavaScript in the action instead of the view to avoid ugly markup in your view:
public ActionResult MyAction()
{
string[] myArray = new string[]{ "hello", "wolrd" };
ViewData["MyData"] = myArray;
ViewData["JavaScriptArray"] = "[" + myArray.Aggregate((current,next) => string.Format("'{0}','{1}',", current, next).TrimEnd(new char[] { ','})) + "]";
// or you can use your favorite JavaScript serialize
return View();
}
Now you can do the following in your view:
<script>
var dataArray = <%= ViewData["MyJavaScriptArray"] %>;
alert(dataArray[0]); // alerts 'hello'
</script>
Like you said it is probably best to get it via Ajax using $.post or $.get or $(element).load() etc...
But if you must save it in the page it is common to save in a hidden field. Asp.net saves things in hidden fields using binary serialization and Base64 but you can save it as a Json string and then use it in your JS.

Using JSON with jRails

I am currently trying to use AJAX in my application via jRails. I am trying to return a JSON object from my controller, and then parse it in my Javascript. I am using json2.js to do the parsing.
Here is the code I currently have:
function getSomething()
{
$.ajax({
type: "GET",
url: "map/testjson",
success: function(data) {
var myData = JSON.parse(data[0]);
window.alert(myData.login);
}
});
}
and in the controller:
class Map::MapController < ApplicationController
def index
end
def testjson
#message = User.find(:all)
ActiveRecord::Base.include_root_in_json = false
respond_to do |w|
w.json { render :json => #message.to_json }
end
end
end
The window.alert simply says 'undefined' (without tics).
However, if I change the javascript to window.alert(data) (the raw object returned by the controller) I get:
[{"salt":"aSalt","name":"",
"created_at":"2010-03-15T02:34:25Z","remember_token_expires_at":
null,"crypted_password":"aPassword",
"updated_at":"2010-03-15T02:34:25Z","id":1,"remember_token":null,
"login":"zgwrig2","email":"zach#zach.com"}]
This looks like an array of size 1, if I'm looking at it correctly, but I have tried just about every combination of JSON.parse on the data object that I can think of, and nothing seems to work.
Any ideas on what I'm doing wrong here?
EDIT
This seems to work fine if there is more than one row in the Users table.
When there are more than one row, what you are deal is a String of your object in json format, not your json object representation.
Really sorry for me english, i want explaind better ...
And if #message is a null object, because there isnt any messange, the render to json dont make anything, and when you are in your JS, you will have a problem because you dont have any response which worker. You must check your #message object. Maybe you can do that:
w.json {render :json => (#message.nil?) ? 0 : #checkpoint }
then, in your callback function you must check if your data response is "0" (like string) or one object.
I can see what you have 2 problems in your code.
It is better if you use $.getJSON to do a JSON request.
jQuery is yet a parser of JSON... then in your callback function you only must do:
$(data).each(function(i, user){
// what you like do with a user
});
In your rails response there a error, is not a w.json { render :json => #message.to_json }, is only w.json { render :json => #message }. Whiout ".to_json".
Hope that helps.

Categories

Resources