Render data from/to Javascript/jQuery - javascript

The goal is to render a json data, which is defined in _data/users.json, to javascript code.
I have a json file in [root folder]/_data/users.json
{
"users"
[{
"username":"test",
"password":"test"
}]
}
And in my jquery, I have tried :
...
{% for user in site.data.users %}
console.log({{ user.username }});
{% endfor %}
...
Which returns me a :
Uncaught SyntaxError : Unexpected token %
Anyone who has some ideas ?

Your liquid tags will only render if you add empty front matter marks to your js file.
js/script.js
---
---
{% for user in site.data.users %}
console.log({{ user.username }});
{% endfor %}
Edit : I just got it ! By reading your code at https://github.com/cui-bo/cui-bo.github.io
You just want to call your _data/user.json file from you javascript, but you cannot because you don't have a _site/_data folder generated.
It's because underscored or doted files and folders are not generated by default, except if you put them in the include array in _config.yml. eg :
include:
- _data
- ....
You can then call _site/_data/user.json with a regular ajax call.
Note: your authentication method is far far from secure.

Related

How to use javascript variable in django if statement (template)?

I'm trying to update my html element with django if statement:
element.innerHTML= `{% if person.name == ${value} %} something {% endif %}`
but I receive an error:
TemplateSyntaxError at /
Could not parse the remainder: '${value}' from '${value}'
I also tried:
`{% if person.name == ${value} %} something {% endif %}`
But the statement doesn't work properly.
Is there anything I can do to combine javascript with Django variables?
Short Answer
No. You can not do this, at least not that easily.
Detailed (slightly) Answer
Django replaces all tags, {{ }} and {% %} with the result at the server (back end) end when the page is rendered, before it gets to the client. Your JavaScript (JQuery?) variable is evaluated by the client (the front end). Django will not know what ${value} is. So, no, you can not use a JavaScript variable in Django template code.
This does not mean you can not achieve what you want, but it must be done in the following way. Your JavaScript code can fetch the value of person.name from a view and then you can do your if statement in the JavaScript file after the response from the fetch is received. The view could return the value of person.name as a JsonResponse

Using ajax post/get call to send current form data to a function

I am trying to keep track of the number of results per page as I navigate through pages by sending them to a function in Django. From my understanding the best way to do this is to use an AJAX GET or POST call, but I can't seem to get it working.
I have a function called myFunction in my views.py file that I want to pass my number of results per page to so I can manage the pagination between pages. Here is my code:
In my javascript file:
function navigate_pages() {
$.ajax({
url: 'autotester/myFunction',
type: 'GET',
data: num_results,
success: function(data){} // I think this is legal to say
});
}
In my HTML:
{% if page.has_previous %}
prev
{% endif %}
{{ page.number }} of {{ page.paginator.num_pages }}
{% if page.has_next %}
next
{% endif %}
In views.py:
def myFunction(request):
if request.method == 'POST':
num_results = int(request.POST.get('num_results'))
paginator = Paginator(tuple_table, num_results)
print "\n\POST: " + str(request.POST) + "\n\n"
else:
num_results = int(request.GET.get('num_results'))
paginator = Paginator(tuple_table, 10)
print "\n\GET: " + str(request.GET) + "\n\n"
where tuple_table is a tuple representation of a dictionary that has my objects that I am paginating. What makes me think it's not working is when I print the request.GET I don't see the num_results parameter that I thought I was passing in with my ajax call. Plus, I get an error for trying to use a NoneType (because there is no num_results parameter). I feel like Django and jQuery/Ajax should make this process relatively simple, but my total lack of experience with JavaScript is making this extremely difficult for me.
If anyone could either point out where I am going wrong or has a link to a more elegant or correct solution that would be greatly appreciated.
The error lies indeed within the javascript, you should pass a collection of key-value pairs instead of just a value.
Furthermore you do not want to put this on a hyperlink element, because the click on a hyperlink element a will trigger a page reload on the new url which in your case is "?page={{ page.next_page_number }}"
You might want to put it on a button element and pass the value in the ajax call:
{% if page.has_previous %}
<button onclick="navigate_pages( {{ page.previous_page_number }})">prev</button>
{% endif %}
{{ page.number }} of {{ page.paginator.num_pages }}
{% if page.has_next %}
<button onclick="navigate_pages({{ page.next_page_number }})">next</button>
{% endif %}
The correct version should be:
function navigate_pages(page_number) {
num_results=5;
$.ajax({
url: '/myFunction',
type: 'GET',
data: {
"num_results":num_results,
"page_num":page_number
},
success: function(data){} // I think this is legal to say
});
}
Working example code

How to pass a list from Python, by Jinja2 to JavaScript

Let's say I have a Python variable:
list_of_items = ['1','2','3','4','5']
and I pass it to Jinja by rendering HTML, and I also have a function in JavaScript called somefunction(variable). I am trying to pass each item of list_of_items. I tried something like this:
{% for item in list_of_items %}
<span onclick="somefunction({{item}})">{{item}}</span><br>
{% endfor %}
Is it possible to pass a list from Python to JavaScript or should I pass each item from list one by one in a loop? How can I do this?
To pass some context data to javascript code, you have to serialize it in a way it will be "understood" by javascript (namely JSON). You also need to mark it as safe using the safe Jinja filter, to prevent your data from being htmlescaped.
You can achieve this by doing something like that:
The view
import json
#app.route('/')
def my_view():
data = [1, 'foo']
return render_template('index.html', data=json.dumps(data))
The template
<script type="text/javascript">
function test_func(data) {
console.log(data);
}
test_func({{ data|safe }})
</script>
Edit - exact answer
So, to achieve exactly what you want (loop over a list of items, and pass them to a javascript function), you'd need to serialize every item in your list separately. Your code would then look like this:
The view
import json
#app.route('/')
def my_view():
data = [1, "foo"]
return render_template('index.html', data=map(json.dumps, data))
The template
{% for item in data %}
<span onclick=someFunction({{ item|safe }});>{{ item }}</span>
{% endfor %}
Edit 2
In my example, I use Flask, I don't know what framework you're using, but you got the idea, you just have to make it fit the framework you use.
Edit 3 (Security warning)
NEVER EVER DO THIS WITH USER-SUPPLIED DATA, ONLY DO THIS WITH TRUSTED DATA!
Otherwise, you would expose your application to XSS vulnerabilities!
I had a similar problem using Flask, but I did not have to resort to JSON. I just passed a list letters = ['a','b','c'] with render_template('show_entries.html', letters=letters), and set
var letters = {{ letters|safe }}
in my javascript code. Jinja2 replaced {{ letters }} with ['a','b','c'], which javascript interpreted as an array of strings.
You can do this with Jinja's tojson filter, which
Dumps a structure to JSON so that it’s safe to use in <script> tags [and] in any place in HTML with the notable exception of double quoted attributes.
For example, in your Python, write:
some_template.render(list_of_items=list_of_items)
... or, in the context of a Flask endpoint:
return render_template('your_template.html', list_of_items=list_of_items)
Then in your template, write this:
{% for item in list_of_items %}
<span onclick='somefunction({{item | tojson}})'>{{item}}</span><br>
{% endfor %}
(Note that the onclick attribute is single-quoted. This is necessary since |tojson escapes ' characters but not " characters in its output, meaning that it can be safely used in single-quoted HTML attributes but not double-quoted ones.)
Or, to use list_of_items in an inline script instead of an HTML attribute, write this:
<script>
const jsArrayOfItems = {{list_of_items | tojson}};
// ... do something with jsArrayOfItems in JavaScript ...
</script>
DON'T use json.dumps to JSON-encode variables in your Python code and pass the resulting JSON text to your template. This will produce incorrect output for some string values, and will expose you to XSS if you're trying to encode user-provided values. This is because Python's built-in json.dumps doesn't escape characters like < and > (which need escaping to safely template values into inline <script>s, as noted at https://html.spec.whatwg.org/multipage/scripting.html#restrictions-for-contents-of-script-elements) or single quotes (which need escaping to safely template values into single-quoted HTML attributes).
If you're using Flask, note that Flask injects a custom tojson filter instead of using Jinja's version. However, everything written above still applies. The two versions behave almost identically; Flask's just allows for some app-specific configuration that isn't available in Jinja's version.
To add up on the selected answer, I have been testing a new option that is working too using jinja2 and flask:
#app.route('/')
def my_view():
data = [1, 2, 3, 4, 5]
return render_template('index.html', data=data)
The template:
<script>
console.log( {{ data | tojson }} )
</script>
the output of the rendered template:
<script>
console.log( [1, 2, 3, 4] )
</script>
The safe could be added but as well like {{ data | tojson | safe }} to avoid html escape but it is working without too.
I can suggest you a javascript oriented approach which makes it easy to work with javascript files in your project.
Create a javascript section in your jinja template file and place all variables you want to use in your javascript files in a window object:
Start.html
...
{% block scripts %}
<script type="text/javascript">
window.appConfig = {
debug: {% if env == 'development' %}true{% else %}false{% endif %},
facebook_app_id: {{ facebook_app_id }},
accountkit_api_version: '{{ accountkit_api_version }}',
csrf_token: '{{ csrf_token }}'
}
</script>
<script type="text/javascript" src="{{ url_for('static', filename='app.js') }}"></script>
{% endblock %}
Jinja will replace values and our appConfig object will be reachable from our other script files:
App.js
var AccountKit_OnInteractive = function(){
AccountKit.init({
appId: appConfig.facebook_app_id,
debug: appConfig.debug,
state: appConfig.csrf_token,
version: appConfig.accountkit_api_version
})
}
I have seperated javascript code from html documents with this way which is easier to manage and seo friendly.
you can do it
<tbody>
{% for proxy in proxys %}
<tr>
<td id={{proxy.ip}}>{{proxy.ip}}</td>
<td id={{proxy.port}}>{{proxy.port}}</td>
<td>{{proxy.protocol}}</td>
<td>{{proxy.speed}}</td>
<td>{{proxy.type}}</td>
<td>{{proxy.city}}</td>
<td>{{proxy.verify_time}}</td>
<td>
<button type="button" class="btn btn-default" aria-label="Left Align">
<span class="glyphicon glyphicon-paste" aria-hidden="true" onclick="copyProxy('{{proxy.ip}}', '{{proxy.port}}')"></span>
</button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
Make some invisible HTML tags like <label>, <p>, <input> etc. and name its id, and the class name is a pattern so that you can retrieve it later.
Let you have two lists maintenance_next[] and maintenance_block_time[] of the same length, and you want to pass these two list's data to javascript using the flask. So you take some invisible label tag and set its tag name is a pattern of list's index and set its class name as value at index.
{% for i in range(maintenance_next|length): %}
<label id="maintenance_next_{{i}}" name="{{maintenance_next[i]}}" style="display: none;"></label>
<label id="maintenance_block_time_{{i}}" name="{{maintenance_block_time[i]}}" style="display: none;"></label>
{% endfor%}
Now you can retrieve the data in javascript using some javascript operation like below -
<script>
var total_len = {{ total_len }};
for (var i = 0; i < total_len; i++) {
var tm1 = document.getElementById("maintenance_next_" + i).getAttribute("name");
var tm2 = document.getElementById("maintenance_block_time_" + i).getAttribute("name");
//Do what you need to do with tm1 and tm2.
console.log(tm1);
console.log(tm2);
}
</script>

javascript-readable json from python

My view computes a json and outputs a json.dumps(), and I'm passing this as the dictionary key data. I'm trying to pass this to a script element in my template, but when rendering, the browser gets it as a python-escaped string{"nodes": [{"count":...... which isn't readable to the javascript. What I need is python to send it as a JS-escaped string, something like this {"nodes": [{"count":.......
I tried str(data) and eval(data) without success. Basically I need python to send the string just as if it were printing it to the console. Thanks
If I understand well, you want to use a json in a template.
In order to do that, you have to disable the escaping, for exemple like this.
{% autoescape off %}
var x={{json_var}}
{% endautoescape %}
Note that instead of using
{% autoescape off %}
{{ my_json }}
{% endautoescape %}
You can simply use a filter :
{{ my_json|safe }}
This works for me:
return HttpResponse(json.dumps({'foo' : 'bar'}, ensure_ascii=False),
mimetype='application/json')

why i can't get the data form Model.all() using google app engine

this is my code in main.py
class marker_data(db.Model):
geo_pt = db.GeoPtProperty()
class HomePage(BaseRequestHandler):
def get(self):
a=marker_data()
a.geo_pt=db.GeoPt(-34.397, 150.644)
a.put()
datas=marker_data.all()
self.render_template('3.1.html',{'datas':datas})
and in the html is :
{% for i in datas %}
console.log(i)
{% endfor %}
but the error is:
i is not defined
so what can i do ?
thanks
The 'i' is interpreted by the templating engine on the server side, so you need:
{% for i in datas %}
console.log({{ i }});
{% endfor %}
In addition to the syntax error sje397 mentioned, the .all() method returns a Query object and I think you'll need to call .fetch(n) or .get() on that to retrieve the actual marker_data objects.
datas=marker_data.all().fetch(100)

Categories

Resources