Error "unexpected char '#' " with rendering Handlebars template - javascript

I have a simple 100% working code from CS50 lecture, which represents the usage of Handlebars. Here it is:
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.11/handlebars.min.js"></script>
<script id="result" type="text/template">
<li>
You rolled:
{{#each values}}
<img alt="{{ this }}" title="{{ this }}" src="img/{{ this }}.png">
{{/each}}
(Total: {{ total }})
</li>
</script>
<script>
// Template for roll results
const template = Handlebars.compile(document.querySelector('#result').innerHTML);
document.addEventListener('DOMContentLoaded', () => {
document.querySelector('#roll').onclick = () => {
// Generate random rolls.
const counter = parseInt(document.querySelector('#counter').value);
const rolls = [];
let total = 0;
for (let i = 0; i < counter; i++) {
const value = Math.floor(Math.random() * 6) + 1;
rolls.push(value);
total += value;
};
// Add roll results to DOM.
const content = template({'values': rolls, 'total': total});
document.querySelector('#rolls').innerHTML += content;
};
});
</script>
</head>
<body>
<input id="counter" type="number" placeholder="Number of Rolls" min="1" value="1">
<button id="roll">Roll</button>
<ul id="rolls">
</ul>
</body>
</html>
When I try to render it in my browser I get "jinja2.exceptions.TemplateSyntaxError: unexpected char '#'".
Obviously the problem is on my computer side. But how to fix it?
I have searched for this problem in the web. One interesting thing I catch was that it is somehow connected with "my server side templating engine". Here is a thread - https://github.com/wycats/handlebars.js/issues/269 . Another guy here (https://github.com/wycats/handlebars.js/issues/1263) says he had similar error because of Pagespeed.
How can I understand which "templating engine" is installed? I have an ordinary Mac and project in virtual environment.
What might be the problem?

This happens because jinja2 reads curly braces as syntax (as variables or block codes).
So, You have to escape jinja in your HTML code.
the methods to do so are following;
The easiest way is to output a literal variable delimiter ({{) is by using a variable expression.
{{ '{{' }}
For bigger sections, it makes sense to mark a block raw. For example, to include handlebars syntax in a template, you can use this snippet:
{% raw %}
<ul>
{{#each vlaues}}
<li>{{ this }}</li>
{{/endeach}}
</ul>
{% endraw %}
For more info check enter link description here

Related

use javascript to display django object

I want to implement below using javascript so row click it will get index and display object of this index.
in django template this is working.
<div>{{ project.0.customer_name}}</div>
<div>{{ project.1.customer_name}}</div>
but the below javascript are not working even I get the correct ID.
var cell = row.getElementsByTagName("td")[0];
var id= parseInt(cell.innerHTML);
// not working
document.getElementById('lblname').innerHTML = '{{ project.id.customer_name}}';
// this is also working but what I want is dynamic base on row click
document.getElementById('lblname').innerHTML = '{{ project.1.customer_name}}';
display django object using index in javascript.
You have to understand what is happening with your code:
Templates like this are processed on the server:
'{{ project.id.customer_name}}'
I believe you do not have project.id on your server side, so you get None in the above line, and the moustache tag becomes smth like an empty string, and actual JavaScript code is like this:
document.getElementById('lblname').innerHTML = '';
It is only now that JS code is executed, and you can imagine what it will do.
What you want is processing moustache tags after the id variable has been set in JS, which is not how stuff works (at least, if you don't have some crazy tool chain).
One way of achieving what you want is to provide the whole project object (or array) to JavaScript by doing the following:
<script>
const project = {{ project|safe }};
</script>
A complete Django template could look like this (I used <span>s instead of table cells:
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>django test</title>
</head>
<body>
{% block content %}
{% for item in project %}
<span data-id="{{ forloop.counter }}">{{ forloop.counter }}</span>
{% endfor %}
<div id="output" style="display: flex; flex-direction: column-reverse;">
</div>
<script>
const project = {{ project|safe }};
const spans = document.getElementsByTagName('span');
const output = document.getElementById('output');
const onSpanClick = (event) => {
const id = parseInt(event.target.getAttribute('data-id'), 10) - 1; // forloop.counter is 1-based, JS arrays are 0-based
const div = document.createElement('div');
div.innerHTML = project[id].customer_name;
output.appendChild(div);
}
Array.from(spans).forEach(span => {
span.addEventListener('click', onSpanClick);
})
</script>
{% endblock %}
</body>
</html>
Another way is the AJAX way: you create an API endpoint on your server side, so that an URL like example.com/api/customer_name/?id=999 responds to you with the name of customer id=999 when you click on some element and trigger an XMLHttpRequest with param id=999.

JS Socket not passing special characters correctly

So, I've seen this question around a lot but the accepted answer isn't working for me. I have this chatroom-like site I'm working on, and while it works fine for most of it (I'm aware of a few other minor issues with my code but they're not part of the question), I'm having issues with the character encoding whenever my JS file is involved and I use sockets. I'll post my HTML and JS files now:
HTML:
{% extends "layout.html" %}
{% block heading %}
<meta charset="UTF-8">
<script type="text/javascript" src="{{ url_for('static', filename='js/chatroom.js') }}"></script>
<link rel="stylesheet" href="{{ url_for('static', filename='css/chatroom.css') }}">
{% endblock %}
{% block body %}
The Chatroom name is <span id="chatroom">{{ chatroom }}</span> and it's code is <span id="code">{{ code }}</span>
<div id="messages">
{% for message in messages %}
{% if message.author == online_user %}
<div class="own">{{ message.message }}</div>
{% else %}
<div class="other"><strong>{{ message.author }}</strong><br> {{ message.message }}</div>
{% endif %}
{% endfor %}
</div>
<textarea id="message" rows=5 cols=50></textarea>
<input type="button" id="send" value="Send">
{% endblock %}
JS:
document.addEventListener('DOMContentLoaded', () => {
var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port);
socket.on('connect', () => {
document.querySelector('#send').onclick = () => {
const message = document.querySelector('#message').value;
socket.emit('send message', {'chatroom': {'code':document.querySelector('#code').innerHTML, 'name': document.querySelector('#chatroom').innerHTML}, 'message': message});
};
});
socket.on('broadcast message', data => {
if (data.message['chatroom']['code'] == document.querySelector('#code').innerHTML) {
const msg = document.createElement('div');
if (data.message['author'] == document.getElementById('current_user').innerHTML)
msg.innerHTML = data.message['message'];
else
msg.innerHTML = `${data.message['author']}: ${data.message['message']}`
document.querySelector('#messages').append(msg);
var listlen = document.getElementById('messages').getElementsByTagName('div').length;
while (listlen > 100) {
var msglist = document.getElementById('messages');
msglist.removeChild(msglist.childNodes[0]);
var listlen = document.getElementById('messages').getElementsByTagName('div').length;
};
};
});
});
Now, when the JS file and socketing isn't involved (for example, if I log into the site) the encoding works fine. However, when the textarea holds special characters such as Á (I'll use the word Ángel on both fields as an example), once I hit the send button (not before), the output is wrong.
Current output: {'chatroom': {'code': 'ouAY7mxCvhXb', 'name': 'UTF'}, 'author': 'Ángel', 'message': 'Ã\x81ngel'}
Expected output: {'chatroom': {'code': 'ouAY7mxCvhXb', 'name': 'UTF'}, 'author': 'Ángel', 'message': 'Ángel'}
The accepted answer for other questions marking this issue was adding the <meta charset="UTF-8"> line on my HTML, but I'm puzzled now since it hasn't worked. I also checked and my JS file is properly encoded to UTF-8 as well.
EDIT:
I'll add the server side code for the socket since it might as well help understand the output better. As seen here, the problem with the encoding only happens from what I pass from data (which can go wrong only on the message's content and chatroom's name, because the chatroom code is automatically generated and never includes special characters).
#socketio.on("send message")
def send_message(data):
data['message'] = data['message'].strip()
if len(data["message"]) > 0:
message = {'chatroom': data['chatroom'], 'author': session['username'], 'message': data['message']}
print(message)
stored_messages.append(message)
local_messages.append(message)
emit("broadcast message", {'message': message}, broadcast=True)
For future references if someone runs into this problem, I found out a way to deal with it. Instead of passing the raw message through the socket and expecting the program to automatically transform it to UTF-8 with the meta tag, I encoded it by adding an encoding function on the JS file and applying it to the message content.
function encode_utf8(s) {
return encodeURIComponent(s);
}
Former line 4 of JS file now looks like this: const message = encode_utf8(document.querySelector('#message').value);
That allows me send it encoded in a way my backend server can properly decode so it returns the expected output by using the urllib.parse library's unquote_plus.
#socketio.on("send message")
def send_message(data):
data['message'] = data['message'].strip()
print(data)
if len(data["message"]) > 0:
message = {'chatroom': data['chatroom'], 'author': session['username'], 'message': urllib.parse.unquote_plus(data['message'])} # <-- Modified line
print(message)
stored_messages.append(message)
local_messages.append(message)
emit("broadcast message", {'message': message}, broadcast=True)

Access Django variable from JavaScript variable

First of all, I will like to say this is my first question here! (pardon me if this is redundant or duplicated)
I am having some problems with calling JS scripts from Django template:
{% for suggestion in suggestions %}
<img class="catalogue-poster" src="{{ suggestion.poster }}" alt="Portada" onclick="
document.getElementById('{{form.title.auto_id}}').value = '{{suggestion.title}}'
document.getElementById('{{form.year.auto_id}}').value = '{{suggestion.year}}'
document.getElementById('{{form.director.auto_id}}').value = '{{suggestion.director}}'
document.getElementById('{{form.rating.auto_id}}').value = '{{suggestion.rating}}'
document.getElementById('{{form.poster.auto_id}}').value = '{{suggestion.poster}}'
document.getElementById('{{form.trailer.auto_id}}').value = '{{suggestion.trailer}}'
document.getElementById('{{form.synopsis.auto_id}}').value = '{{suggestion.synopsis}}'
document.getElementById('{{form.cast.auto_id}}').value = '{{suggestion.cast}}'
" />
{% endfor %}
So, first of all, how can I declare a function outside. I'm a C developer, sorry for my ignorance.
I've tried to create a script outside, such as
<script>
function foo() {
console.log('Hey');
});
</script>
And invoke it this way:
<img class="catalogue-poster" src="{{ suggestion.poster }}" alt="Portada" onclick="foo()"/>
But this simple thing that works on pure HTML, with django templates does not seem to work...
On the other hand, the real question was, is there a way to access a Django variable passed in render with a js variable?
Such as:
const jsVariable = 'title';
document.getElementById('{{form.jsVariable.auto_id}}').value = '{{suggestion.jsVariable}}'
I have not found any way to accomplish this, maybe there is another great idea!
I have tried one example. where is send a variable from python script and access its value in JavaScript
1) In views.py
from django.shortcuts import render
def home_view(request):
var_name = 'hello'
return render(request, 'home.html', {'var_name':var_name})
2) In html file(home.html)
<html>
<h1>Home Page</h1>
<input type="button" value="Submit" onclick="fun()">
<script>
function fun(){
console.log('hello world '+ '{{var_name}}' );
}
var temp = '{{var_name}}';
console.log(temp + 20);
</script>
</html>
If i click submit button ( hello world hello ) is printed in console.
I stored value of var_name in temp which can be further used.
From your example, it looks you want to programmatically access a Django model's attribute in Javascript.
The main takeaway is that you first need to expose the data structure you want to access (i.e. the model) in Javascript.
Here's a simple, redacted, proof-of-concept you can try.
import json
def my_view(request):
obj = MyModel.objects.get(1)
obj_dict = {
"foo": obj.foo,
"bar": obj.bar,
}
return render(request, 'my_view.html', context={'obj_json': json.dumps(obj_dict)} )
<script>
var obj = {{obj_json}};
var field = 'foo';
console.log(obj[field]);
Check out Convert Django Model object to dict with all of the fields intact for a run-down on options to serialize Django models into dictionaries.
Well, finally I found a solution for both exposed problems.
First of all, the script function I declared was not working because it seems that there is an attribute called autocomplete (see autocomplete HTML attribute)
So, you can not declare a JavaScript function with this name, my fail.
Uncaught TypeError: autocomplete is not a function
Finally, the simple solution I found was passing an array of dicts to the template:
return render(request, 'example.html', {'form': form, 'suggestions': suggestions })
And then in the template:
{% for suggestion in suggestions %}
<img src="{{ suggestion.poster }}" onclick="autocompleteMovie({{suggestion}});" />
{% endfor %}
<script>
function autocompleteMovie(suggestion){
for (let field in suggestion)
document.getElementById('id_' + field).value = suggestion[field]
}
</script
Which, comparing it with the question, really simplifies the problem.

How to print a list on another webpage after passing it with localStorage?

This is my home.html code. I pressed a button in a different .html file. The names are added and stored in an array in the other file and passed thru localStorage and parsed in home.html. Then I looped thru the array to print the names. My issue is on the webpage on the server the names show but after I submit a name it replaces the previous name on the webpage. It's not making a list with one name followed by the other. Is there something I can add with javascript so the names don't keep updating or refreshing? Thanks!
home.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title> Home Page </title>
{% extends "navigation.html" %}
{% block content %}
<p> List of Names: </p>
<ul id="List"></ul>
</head>
<body>
<script>
var LL = JSON.parse(localStorage.getItem("newList1"));
document.getElementById("List").innerHTML += "<li>" + LL[LL.length-1] + "</li>";
</script>
</body>
{% endblock %}
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title> Add Name Page </title>
{% extends "navigation.html" %}
{% block content %}
<p> Add a name: </p>
<form action="home.html">
<input type='text' input name='name' id='addname'>
<input type="button" id="add" value="Submit" onclick="passVal()">
</form>
<ul id="nameList"></ul>
add_name.html
</head>
<body>
<script>
function passVal() {
newList = [];
var newName = document.getElementById("addname").value;
newList.push(newName); //Note: push in javascript assigns value to newList. If I set this to a variable, it would only store the number of elements in that list
localStorage.setItem("newList1", JSON.stringify(newList));
}
</script>
</body>
{% endblock %}
</html>
Thanks for updating your post and including the add_name.html file. The main problem lies there. What you do in the passVal function is to start a new array, then add one value to the array, and then setting the list in the local storage to that array. Therefore the array in the local storage always only has one entry.
Rather than setting the newList variable to an empty array, you should set it to the list of items that are already in the local storage:
add_name.html
<script>
function passVal() {
var previousValue = localStorage.getItem("newList1"); // Get the previous value
var newList;
if(previousValue) {
newList = JSON.parse(previousValue);
} else {
newList = []; // If nothing is in the local storage until now, start with an empty list.
}
var newName = document.getElementById("addname").value;
newList.push(newName);
localStorage.setItem("newList1", JSON.stringify(newList));
}
</script>
</body>
{% endblock %}
Then in the home.html, you need to loop through the values:
var LL = JSON.parse(localStorage.getItem("newList1"));
for(let item of LL) {
document.getElementById("List").innerHTML += "<li>" + item + "</li>";
}

Handlebars.js printing object doesn't work

this might be real simple but I've tried many examples and still couldn't make it working.
So I've got this code to check if the entry is written by the author and if so, I want to print it into template.
function printTitltes() {
var currentUser = JSON.parse(localStorage.getItem('currentUser'))
var author = currentUser["username"];
var allEntries = JSON.parse(localStorage.getItem("allEntries"));
var template = Handlebars.compile($("#template").html());
var authorEntry;
if (!allEntries) {
return
} else {
for (var i=0; i<allEntries.length; i++) {
if (allEntries[i]["author"] === author) {
authorEntry = allEntries[i];
$("#titleArea").append(template(authorEntry));
}
}
}
}
And my template is:
<script type='text/template' id='template'>
<ul class="entries-list">
{{#each auhorEntry}}
<li data-id="{{ID}}">
<span> {{date}} </span>
{{title}}
</li>
{{/each}}
</ul>
</script>
When executed, all I got is an empty template. I've must have been sending a wrong object to the template but couldn't grasp how could I do it right. Anyone whould spot it for me please?
<script type="text/x-handlebars-template" id="template">

Categories

Resources