What I am trying to do is pretty simple: yet something has clearly gone awry.
On the Front-End:
function eval() {
var x = 'Unchanged X'
$.get("/", { entry: document.getElementById('entry').value },
function(data){
x = data;
}
);
$("#result").html(x);
}
On the Back-End:
class MainHandler(webapp.RequestHandler):
def get(self):
path = os.path.join(os.path.dirname(__file__), 'index.html')
if self.request.get('entry') != '':
#self.response.out.write({'evalresult': self.request.get('entry')})
self.response.out.write(request.get('entry'))
else:
self.response.out.write(template.render(path, {'result': 'Welcome!!'}))
def main():
application = webapp.WSGIApplication([('/', MainHandler)],
debug=True)
wsgiref.handlers.CGIHandler().run(application)
Yet, apparently the function is never being called and #result gets set to 'Unchanged X'. What am I missing?
NOTE: The callback is NOT being called. I have verified this by placing an alert("Test") within the callback function. Any ideas anyone?
$("#result").html(x); goes in the get() callback
If the callback is not running you can try changing the $.get into a $.ajax() call, and adding an error callback, to see if the server is returning an error.
Or better yet, check in the "net" panel in firebug to see what the server response is, which might help you track down what the issue is on the back end.
Also once the issue is fixed, you might want to replace the $.get with a simple $().load which would take the data and place it into the div automatically:
$('#result').load('/', { entry: document.getElementById('entry').value });
EDIT: I suppose the following would be a more jQueryish way of writing it:
$('#result').load('/', { entry: $('#entry').val() });
First we have the silly mistake:
<font size="3" face="Trebuchet MS">Speak Your Mind: </font><input type="text"
size="60" id="entry"/> <img valign="bottom" src='/assets/cognifyup.png'
onMouseOver="over()" onMouseOut="out()" onMouseDown="out(); evaluate();"
onMouseUp="over()"><br><br>
Semicolons are required after the calls to over() and out() (roger that? --- sorry couldn't resist)
Secondly (the much more subtle problem):
If we ever need intend to translate the get() into a getJSON() call, (which you might have noted was my original intent from the commented python code that returns a dict), then we need to wrap a str() call around self.request.get('entry'). Hence,
self.response.out.write({'evalresult': self.request.get('entry')})
becomes:
self.response.out.write({'evalresult': str(self.request.get('entry'))})
As strings from an HTML field translate to unicode text in Python, at the back-end, we apparently need to convert it to a Python string (as getJSON() apparently doesn't like Python's representation of a unicode string -- any ideas why this this is the case anyone?).
At any rate, the original problem has been solved. In conclusion: any JSON object with a Python unicode string will not be accepted as a valid JSON object and will fail silently -- a nasty gotcha that I can see biting anyone using JQuery with Python on the server-side.
Related
I'm not sure I really understand why this is being treated this way - is it because fetch tries to encode all variables? If so how do I fix it?
This fetch URL will work when the only variables I put into it are coordinates (numbers). In my overall code, I have a string that I encoded myself from an array that can have a varying bracket structure, so that is the reasoning.
When I copy and paste this into a browser and paste in my variable where it should go, it works. But the fetch doesn't work.
Is fetch trying to do something more to my variable that already has encoding? I don't understand, because the non-variable part of the string is already encoded, so I'm assuming the issue has to do with how fetch is treating the variable.
Anyway, see the structure where the variable is in my URL too, I guess (sorry it's long):
JS:
let testString = "%5B%5B%5B-89.0000%2C40.5555%5D%2C%5B-89.1111%2C40.5555%5D%5D%5D";
fetch('https://hazards.fema.gov/gis/nfhl/rest/services/FIRMette/NFHLREST_FIRMette/MapServer/1/query?where=&text=&objectIds=&time=&geometry=%7B%22paths%22%3A+' + testString + '%7D&geometryType=esriGeometryPolyline&inSR=4326&spatialRel=esriSpatialRelIntersects&distance=&units=esriSRUnit_Foot&relationParam=&outFields=*&returnGeometry=false&returnTrueCurves=false&maxAllowableOffset=&geometryPrecision=&outSR=&havingClause=&returnIdsOnly=false&returnCountOnly=false&orderByFields=&groupByFieldsForStatistics=&outStatistics=&returnZ=false&returnM=false&gdbVersion=&historicMoment=&returnDistinctValues=false&resultOffset=&resultRecordCount=&returnExtentOnly=false&datumTransformation=¶meterValues=&rangeValues=&quantizationParameters=&featureEncoding=esriDefault&f=pjson')
.then(function (response) {
return response.json();
})
.then (function (data) {
appendData(data);
})
.catch(function (err) {
console.log('error: ' + err);
});
Edit: I even tried straight up running the fetch with the string from my variable, and it worked. So it has to do with the variable - it just doesn't make much sense.
My full messy testing code (add the coordinates to list then run)
Edit 2: See my below answer. In my actual code I had add/remove options functionality that was causing an issue in my function to fetch the data. I needed these options to build my string used in the fetch. I used preventDefault() to fix the problem - but I'm still not positive on why it works with that specifically.
I found my issue, so I'm answering my question - I welcome any explanation though.
I posted a jsfiddle of my code but it's a mess, but to sum up what was going on:
I had a list of options that the user could add to add coordinates, and then run those. In my problem code, these options were referenced under a GetData() function which ran my fetch. Outside of that GetData() function, these options were created, but I didn't think that was the problem because the brackets were closed around both the add and remove an option functionalities.
I had a button for add, and a button for remove. These functionalities were made, for example, with:
btnRemove.onclick = (e) => {
e.preventDefault();
// add/remove/getselected/etc functionalities here
}
I created a function just like this to replace my GetData() function, reading:
linearGPSsubmit.onclick = (e) => {
e.preventDefault();
}
The preventDefault() was originally to help aid the add/remove functionality.
That code above executes on submit button click. What I don't fully understand is why the fetch runs at all when preventDefault() is used? Shouldn't it stop me from submitting the form? Nevertheless... no more 404 and the fetch fetches. Cool. It feels wrong, but it works.
I actually work on a tool named jedox. With this tool I can make macro(like excel) but in PHP. Jedox provide some example of macro and in one of these example there is this code:
function test()
{
return array(array('eval', "(function(){
console.log('salut')
}())"));
}
It's a PHP code that run JS code. But the problem is I don't know how this code work, the only thing I know about it is that it execute JS code but can't return anything, so if you put return in the code it will not return any value. I have no way to retrieve the value returned.
So my question is how should I supposed to retrieve a value from this ?
PS: if I try to investigate about the value returned by test() I get an array nested with another array with those 2 values 'eval' and the function.
PS2: Apparently you can't run this code correctly with traditional tool. So to help me you should have jedox, I guess :/ ...
On the client side, someone must be getting those two strings and executing them. The PHP code ("host side") is not actually doing that.
You may could put the Javascript code into a file. Then execute the file using NodeJS and get the value.
For example:
function test() {
file_put_contents('test.js', <<< TEXT
(function(){
console.log('salut')
}())
TEXT);
return shell_exec('node test.js');
}
echo test(); // Return: sault
Also notice that in most shared hosts and servers shell_exec function is disabled by default. You can enable it through you php.ini.
I tried to access the my C# method in my JavaScript, but this method is not accepting the local parameters and saying, the parameter does not exist in the current context.
var typeofServiceNumber = $("#serviceType").val();
#MyStaticClass.StringEncoding(Convert.ToString(typeofServiceNumber));
The above typeofServiceNumber is not recognized by the method
Razor code is executed server side before the HTML is returned in the response stream.
Javascript is executed client side within the resultant HTML.
Therefore you cannot pass a Javascript variable through to a Razor method, and you receive the message that typeOfServiceNumber is not recognized.
To be recognized, it would either need to be handled server side via data being passed to the View (ViewBag, Model etc), or it would need to be declared and assigned to within Razor tags on the page itself.
EDIT: to clarify the last point:
var typeofServiceNumber = $("#serviceType").val();
#MyStaticClass.StringEncoding(Convert.ToString(typeofServiceNumber))
The first line you have here is all happening in the browser of the end user.
The second line is all happening on the server. You see the error message because you are trying to pass "typeofServiceNumber" to your method, and the variable isn't even declared at that point.
Without knowing exactly what you're trying to achieve it's hard for me to give a precise answer as to how to solve your problem. But here are two possibilities:
1) You know what $("#serviceType").val() is going to be before you serve the web page to the end user because that value is being set server side.
Controller:
public ActionResult MysteryController()
{
...your code here to work out what the service type is...
ViewBag.serviceType = serviceType;
return View();
}
View:
...lots of lovely html and such...
<script>
#MyStaticClass.StringEncoding(Convert.ToString(ViewBag["serviceType"]));
</script>
I can't see what the output of #MyStaticClass.StringEncoding() is but I have to assume at this point that it is doing whatever it is supposed to do.
Although the logic is split between the controller and the view, all of this is happening server side.
The second half of my point "or it would need to be declared and assigned to within Razor tags on the page itself." refers to the fact that one variation of this method could involve manipulating data in the View itself by enclosing it in a Razor code block like this:
#{
var typeofServiceNumber = #MyStaticClass.StringEncoding(Convert.ToString(ViewBag["serviceType"]));
}
The alternative, which I did not really address originally is:
2) You don't know what the value of $("#serviceType").val() is going to be before the page is loaded because it is being set by the end user and your function needs to be used before the data is submitted to the server:
If that's the case then #MyStaticClass.StringEncoding(Convert.ToString(typeofServiceNumber)) is no good to you, you will have to replicate the function in JavaScript and include it in the webpage itself.
This question may be a bit confusing, so let me give you some background. Eel is a Python module where you can take functions made in Python and use them in Javascript, and vice versa. What I want to do is take a json made from a Python function, put it in Javascript, and make a table based on the json that was taken from the Python side. Here's an example.
python.py
def json_example():
json = [
{
"key": "value1"
},
{
"key": "value2"
}
]
return json
js.html
<body>
<div></div>
</body>
<script>
function js_example() {
# This is where the function from Python is called
var json_obj = eel.json_example();
var tbl = $("<table/>").attr("id", "example_table");
$("div").append(tbl);
for(var i=0; i<json_obj.length; i++){
var tr="<tr>";
var td="<td>"+obj[i]["key"]+"</td></tr>";
$('#example_table').append(tr+td);
}
}
I tested both of these functions out separately with a few changes and they both work. However, here's where the problem starts. For some reason, the Javascript part is not getting anything from the function it calls from the Python code. the variable "json_obj" should be equal to the json I made in the Python function, but for some reason the return value of the function isn't creating tangible data that can be manipulated in the Javascript, basically returning nothing. And the eel transfer itself works as well. If you replace "return" with "print", it will print the json in the console.
Also, please don't tell me to just put the json itself in the Javascript. I have a reason for needing the json to come from the Python side.
So basically, here's my question: how do you get a Python function to create a value that can be manipulated in Javascript?
The problem is that when eel exposes a function what it actually does is it creates a new function that will return a promise containing the return value of your python function.
So you should have something like this instead:
let json_obj = '';
eel.json_example()(x => json_obj = x);
If you need more help on callbacks, refer to https://github.com/ChrisKnott/Eel.
Convert to json within Python if you're calling Python to begin with and send json to JS in the return value.
See: https://github.com/ChrisKnott/Eel/tree/master/examples/03%20-%20sync_callbacks
To do a synchronous operation that will take time to complete in Python and then return a value into JS, use;
let n = await eel.py_random()();
which is really
let mySlowReturnValueFromPython = await eel.myExposedPythonFunction()();
In fact I tried to code in my own promises and I was getting back garbage that looked like eel promises. The browser maintains a thread while you call this way, so the user can kick off a long Python operation and still interact with the GUI.
I'd note that you can still call things that update the GUI asynchronously. If you have a bunch of Python functions which return ready-made HTML as I do, then you can kick them all off in a row without waiting and they will update the divs whenever they return. I use this;
eel.expose(updateDiv);
function updateDiv(newData, divToUpdate)
{
var fieldToUpdate = document.getElementById(divToUpdate)
fieldToUpdate.innerHTML = newData
}
Then I call my Python function which gets the data synchronously, packs it up into a ready-made HTML chunk for the GUI, and then calls updateDiv from Python. I'm actually really enjoying the power that this "ping pong" interaction between a synchronous codebase and an asynchronous one give me when working with a GUI. Worlds better than arfing about with TK.
I hope this helps you and you can struggle less with it than I did. Once you understand how this works, Eel is really great. It apparently handles sync for you, just hand it a blank callback (or whatever black magic that is). What a great lib! Eel is just perfect for locally hosted GUI's. I'd like to see a better GUI framework than HTML/CSS/JS - the truth is that there really isn't one, those things are so well tested and stable, with so many available examples for whatever you could want to create.
I'd really like to see this become the native Python GUI solution. Browsers are extremely cross-platform and the only problem to solve when porting becomes interfacing the browser to Python.
I need to call a controller function from javascript on my gsp.
I have read different solutions from hundreds of places but none worked.
The problem which I found closest to mine was this.
But I am not repeating the same mistake as this and thus the solution didn't help.
I have a tag like this which calls the javascript function
<g:select name="poNumber" noSelection="['':'Select PO Number']" from="${com.rerq.PurchaseOrder.list()}"
onchange="getProject(this.value)" />
And the javascript function looks like this
function getProject(poNumber){
var projectName = document.getElementById("projectName");
var newData = ${remoteFunction(controller: 'sow', action: 'getProject', params: ['poNumber':poNumber])};
}
And the function I need to call is
def getProject(String poNumber) {
String projectName = Sow.find("from Sow as s where s.poNumber=?", [poNumber])
return projectName
}
The controller function might have mistakes as I am completely new to groovy and grails. But my understanding is that the control isn't reaching here so this should not be the cause of any problem.
I am getting below exception
No signature of method: remoteFunction() is applicable for argument types: (java.util.LinkedHashMap) values: [[controller:sow, action:getProject, params:[poNumber:null]]]
I tried using remoteFunction() in g:select itself but it threw another exception which said
Attribute value quotes not closed ...
even though they were.
Any help is greatly appreciated.
To use remoteFunction with Grails 3 you need to add the ajax-tags plugin: org.grails.plugins:ajax-tags:1.0.0
Actually you can have your gsp recognize some Grails functions inside your js if the script is inside the gsp and anything you need for your js is created on the server side. In your case it seems you want to do an ajax call so you could have the following.
project.gsp (Consider that you already loaded jQuery)
<g:select name="poNumber" noSelection="['':'Select PO Number']" from="${com.impetus.rerq.PurchaseOrder.list()}"
onchange="getProject(this.value)" />
And in the same file you have
<script type="text/javascript">
function getProject(poNumber){
jQuery("#yourTarget").load("${createLink(action: 'getProject')}",{poNumber: poNUmber},function(response, status, xhr ){
if ( status == "error" ) {
alert("No data loaded.")
}
});
}
</script>
As you see a gstring in load("${}",... is used because it will be parsed in the server and at client side your actual js it will parse to load("yourcontrollerName/getProject",..... Why not code the url directly in the js? Because by using createLink() it is less likely to make reference mistakes.
EDIT
If you want to do this but using an external js file, you would need a way to pass the url, to the js, and use a simple load function. So something like this will be helpful
<g:select name="poNumber" noSelection="['':'Select PO Number']" from="${com.impetus.rerq.PurchaseOrder.list()}"
onchange="getProject(this.value, \'${createLink(action:'getProject')}\')" />
Once on the server onchange="getProject(this.value,\'${createLink(action:'getProject')}\')"would be parsed to onchange="getProject(this.value,'yourController/getProject')". Be wary that I might have messed up the ""s and ''s so verify your html output.
And you would need a js function that accepts the uri
function getProject(value, targetUri){
....
}
What you need to review is when is your function needed, on the server o in the client;if the functions are inside a gsp or not; And if not available, how could you pass data to them.
You cannot access grails's controller from javascript. I haven't tested it but this might work.
<g:createLink controller="sow" action="getProject", params="['poNumber':poNumber]"/>
Also, if you use Google Chrome's developer's tool, you will see how your javascript code is displayed. Make sure it is in right syntax.