Background:
I am using PlayFramework to create a webapp (workflow) system which uses the Twirl template engine (scala based) to create my views.
Problem:
I need to store JavaScript variables inside of my template (which renders HTML), where in a separate JS file(s), I will perform operations on later.
Any existing solutions?
This can be done according to these SO questions I found:
This one creates a map where data is stored in and later retrieved.
This one creates a simple example of storing a variable in an inline script tag to use later
Finally, this one uses a more concrete example of storing data in data structures in the template and accessing the data structures in JS after being parsed.
The example created by an active SO contributor, Biesior, helping many developers with Play! Framework questions
To summarise what should be done:
Pass in some data into template
#(myData: DataObject)
Add data passed in an into a script tag with a JS name
<html>
<body>
<script>
let someJSDataName = #myData
</script>
</body>
</html>
Use in JS / Google Chrome Dev console
someJSDataName.toString
This should display some result!
But what is the problem?
Let me show you.
Rendered Html:
//...
<div class="BoxMediumBlue" style="padding: 20px;">
<script>
let facultyDepartments = {Science=[Computer Science, Physics], Biology=[Zooology]};
</script>
<br>
<div class="container-heading">
//...
When attempting to access this data in my Google Chrome dev console:
facultyDepartments
VM1209:1 Uncaught ReferenceError: facultyDepartments is not defined
at <anonymous>:1:1
Just an FYI, using var makes no difference
Am I doing something wrong?
Your definition of facultyDepartments uses syntax that the JS engine doesn't understand:
let facultyDepartments = {Science=[Computer Science, Physics], Biology=[Zooology]};
Objects should contain key-value pairs, where the keys and values are separated by :s, and where the keys and values, when strings, have delimiters like " or '. You should try to get the template to render JSON instead, for example:
let facultyDepartments = {"Science":["Computer Science", "Physics"], "Biology":["Zooology"]};
console.log(facultyDepartments.Science);
(JSON is technically a way of formatting strings, but inserting a JSON string without delimiters so that it's parsed as an object literal works too)
[Log] {"image":"/SAS/default/download/uploads.image.85f2588e34848596.30362d32353033392e746966.tif","filename":"/SAS/default/download/06-25039.tif","start":1437444049436,"width":1080,"height":734,"events":[{"colour":"#0000ff","width":3,"erased":false,"points":[{"x":795,"y":256,"time":1437444050332},{"x":754,"y":260,"time":1437444050338},{"x":642,"y":271,"time":1437444050355},{"x":466,"y":291,"time":1437444050372},{"x":268,"y":318,"time":1437444050389},{"x":148,"y":344,"time":1437444050406},{"x":101,"y":359,"time":1437444050423},{"x":92,"y":369,"time":1437444050441},{"x":104,"y":377,"time":1437444050458},{"x":161,"y":381,"time":1437444050475},{"x":268,"y":381,"time":1437444050492},{"x":405,"y":366,"time":1437444050509},{"x":513,"y":346,"time":1437444050527},{"x":557,"y":328,"time":1437444050544},{"x":554,"y":315,"time":1437444050562},{"x":529,"y":305,"time":1437444050579},{"x":484,"y":301,"time":1437444050596},{"x":435,"y":304,"time":1437444050613},{"x":401,"y":316,"time":1437444050630},{"x":388,"y":329,"time":1437444050648},{"x":389,"y":342,"time":1437444050665},{"x":406,"y":356,"time":1437444050682},{"x":430,"y":367,"time":1437444050699},{"x":449,"y":370,"time":1437444050716},{"x":457,"y":370,"time":1437444050733},{"x":458,"y":370,"time":1437444050751},{"x":457,"y":368,"time":1437444050769}]}],"end":1437444051345,"elapsed":1909} (experiment, line 164)
This is the String which I need to save.I have to perform two tasks:
Save the string on the system and display it as a View(as it is)
Display a specific argument only. Such as "elapsed" in this string as a View.
How do I do this?
Thanks for the help! :)
Depending on what/how you want to display your JSON string, you have some different options. If you want to display the raw JSON string stored in variable strJSON, you can just inject it into the view at the appropriate location with {{ =strJSON }}. web2py has a built-in method BEAUTIFY which makes injected variable outputs look nicer, i.e. you'd call {{ =strJSON }} to take advantage of this.
If you want to only print certain elements &/or manually format it yourself, as in your second question, I'd recommend converting the JSON string back into a Python object. web2py has a module called SimpleJSON which does all the JSON encoding/decoding magic for you. It's located at gluon.contrib.simplejson. To convert a JSON string to an object, you would use the function 'loads'. For converting a Python object into a JSON string, use 'dumps'. So, for example:
{{ import gluon.contrib.simplejson as sjson }}
{{ myObj = sjson.loads(strJSON) }}
{{ strJSON = sjson.dumps(myObj) }}
Now that your JSON string is stored as an object, you can access elements like you normally would in Python based on whether your object is a list, dictionary, etc. So, for example, if we turn the JSON from your screenshot into an object, we can access the image name as so in Python:
imgname = myObj["image"]
Similarily, you can access the first event via:
my_events = myObj["events"][0]
Which is a dictionary of 'colour', 'width', etc.
I'm using json.net to automatically deserialize my json objects into .net objects. As long as the json is valid, it works a treat.
But I'm having trouble with html in the json...the special characters and quotes are making the json invalid.
How do I encode or escape the html reliably so it's valid json? I need a way to do it with just javascript because it's the client side sending to the server side.
Edit
Just to give an example of my use case, I have a WYSIWYG in my app and I need the input from that included as part of a json object to be posted to my server.
Just to give an example of my use case, I have a WYSIWYG in my app
and I need the input from that included as part of a json object to be
posted to my server.
Assuming you have the user input in a string variable:
var userInput = 'Any crap with HTML you can imagine';
You can simply JSON encode this in order to transport it as a valid JSON string:
var json = JSON.stringify({ value = userInput });
Now the resulting object will look like this:
{
"value": "Any crap with HTML you can imagine"
}
and on your server simply map this to a POCO with a plain Value string property. The JSON.stringify method will ensure to properly serialize any input to a valid JSON string.
This being said, I don't quite understand your need of wrapping the user input in a JSOn string and then deserializing it back on the server with JSON.NET. I would rather send the raw input as-is. This way you would get exactly the same value on the server without the overhead of JSON serialization.
How to pass dictionary from Jinja2 (using Python) to Javascript ?
I have dictionary in Python and when I render template I need to use that dictionary with Javascript, I passed from Python
template = JINJA_ENVIRONMENT.get_template('sm.html')
self.response.write(template.render(values=values))
but how to store them in Javascript variable inside html page.
Use the json module to turn the Python data into JSON data; JSON is a subset of JavaScript * and does fine as a JavaScript literal:
import json
js_value = json.dumps(python_value)
and render the js_value in the template.
If you need the JSON data to be HTML safe too, you'll need to add some replacements:
js_value = (json.dumps(python_value)
.replace(u'<', u'\\u003c')
.replace(u'>', u'\\u003e')
.replace(u'&', u'\\u0026')
.replace(u"'", u'\\u0027'))
*JSON allows for U+2028 and U+2029 characters which JavaScript literals can't contain, but the json.dumps() function escapes all non-ASCII codepoints by default, so provided you don't disable ensure_ascii you are fine.
You want to store it as Json. This is javascript's native format, so you can pass that directly to a variable.
This question already has answers here:
JavaScript raises SyntaxError with data rendered in Jinja template
(3 answers)
Closed 13 days ago.
I am using Flask with Jinja2 as templating language.
How do you convert a multidimensional Python structure to a corresponding structure in javascript using Jinja2?
Example (Python/Flask):
pyStruct = [{key1:value1, key2:value2, [{subkey1:subvalue1, subkey2:subvalue2,}]},
{key1:value1, key2:value2, [{subkey1:subvalue1, subkey2:subvalue2,}]},]
render_template('jinjatemplate.html', pyStruct=pyStruct)
Example (Jinja2):
??
I guess what I'm asking is, can it only be done by creating convoluted loop constructs in Jinja2, or am I missing a shortcut somewhere?
If the answer is, yes, one has to use convoluted loops in Jinja2, then it's probably a lot easier to just create the javascript code directly in python and pass this to Jinja2 for inclusion.
But that seems to defeat the purpose of using a template language like Jinja2 somewhat...
I tried (Jinja2):
{{ pyStruct|safe }}
...and this actually works as long as nothing is unicode, and doesn't stray out of Ascii land (which it usually does in my case).
Oh, and if you wonder why pass this kind of structure? I find I often want to pass fairly complicated structures to javascript to be used by menus and other complicated selection interfaces.
You can use the json module, either as a Jinja filter ou directly passing the results of json.dumps() to your template.
pyStruct = [{key1:value1, key2:value2, [{subkey1:subvalue1, subkey2:subvalue2,}]},
{key1:value1, key2:value2, [{subkey1:subvalue1, subkey2:subvalue2,}]},]
render_template('jinjatemplate.html', json_struct=json.dumps(pyStruct))
In the template:
var myStruct = ({{ json_struct|e }});
Warning: I'm a bit unsure about the escaping bit (|e filter). You might want to check that the <, >, & characters are properly escaped with unicode escape sequences rather than xml entities.
Serialize it using json:
from django.utils import simplejson
pyStruct = [{'key1':'value1',
'key2':'value2',
'key3':[{'subkey1':'subvalue1', 'subkey2':'subvalue2'}]},
{'key1':'value1',
'key2':'value2',
'key3':[{'subkey1':'subvalue1', 'subkey2':'subvalue2'}]}]
print simplejson.dumps(pyStruct)
Flask likely has an equivalent way to json serialize data. This can also be done using loop constructs in jinja2, but is many times slower than using json.