use for loop with JavaScript template literal function - javascript

I have a web app, frontend using normal HTML5, backend using Django.
In the frontend page, I have a JavaScript template literal function. Which is supposed to render all the individual value into a selection box of a queryset passed from backend to a bootstrap table.
view.py:
def view_material(request):
query_results_publisher = Publisher.objects.all()
return render(request, 'material/index.html', context={'Publisher':query_results_publisher})
publisher is query set: <QuerySet [<Publisher: >, <Publisher: Arkib Negara Malaysia>, <Publisher: DBP>, <Publisher: Edward Elgar>...]
index.html(bootstrap table + javascript template literal function):
...
<th class ='publisher' data-field="book.publisher" data-formatter="renderPublisher">Publisher</th>...
<script>
var Publisher = "{{ Publisher }}";
var publisher = '';
function renderPublisher(value) {
return `select style="width: 7em" name="" id="">
for (publisher in ${Publisher}) {
<option value="eText" ${(value === 'eText') ? 'selected="selected"' : ""}>
publisher</option>}</select>}`
</script>
But my for loop in javascript template literal function is not working, seems like I have some problem with the template literal usage in for loop.
How to correct my function?

You need check value of Publisher before loop it, if it is array, use for-of, if is object, use for-in. But in your case, I saw you print publisher, not it property, so i guest it must be array ( change if im wrong ) . Something like :
var publishers = "{{ Publisher }}"; // no capital for variable' name
function renderPublisher(value) {
var renderString = 'select style="width: 7em" name="" id=""'
for (publisher of publishers) {
renderString += `
<option value="eText" ${value === 'eText' ? "selected" : ""}> ${publisher}</option>`
}
renderString += '</select>'
return renderString;
}

Related

Can you do math on variables inside double curly braces in a html script?

this is a html file i currently have:
<!DOCTYPE html>
<html>
{{unit.unit_class}}
<div id="stats"></div>
<script>
var str ="", i;
for(i = 1; i<=99; i++)
{
str = str + "<p>level " + i + ": hp {{unit.hp_current}}</p>";
}
document.getElementById("stats").innerHTML = str;
</script>
</html>
that bit in the for loop {{unit.hp_current}} is a decimal number. It's value is currently set to 0.00, which it does display on my page. I was wondering if I was able to do arithmetic on this variable inside the double curly braces. I tried doing something like {{unit.hp_current + 3}} or {{unit.hp_current}} + 3 in order for the program to display 3.00 instead of 0.00, but it doesn't seem to work. Is this something I need to make a function for instead in my views.py?
No, django template language does not support operations on variables. You will need to calculate values in-code, then pass them to the template. Another way is to use a function in a template, that would calculate the value you need:
When the template engine encounters a variable, it evaluates that
variable and replaces it with the result.
...
If the resulting value is callable, it is called with no arguments.
The result of the call becomes the template value.
https://docs.djangoproject.com/en/3.0/ref/templates/language/
Yet another option is to use filters:
https://docs.djangoproject.com/en/3.0/ref/templates/language/#filters
The vairables are directly set into the HTML using the template engine , I think you should store it in some variable and than do some other arthematic operations,
<!DOCTYPE html>
<html>
<div id="stats"></div>
<script type="text/javascript">
let template_value = "{{unit.unit_class}}";
let hp_current = parseFloat(template_value);
let str = '';
for(let i = 1; i<=99; i++)
{
str = str + `<p>level ${i} hp ${hp_current}</p>`;
}
document.getElementById("stats").innerHTML = str;
</script>
</html>
you can put this kind of substitution into your JavaScript.
It is kind of the dynamic javascript , Here we have stored the value in a variable called hp_current , Now you can use this for arthematic , Even you should use the backticks in here to concatinate the variables . Try out the code up here if it can work for you !
Here is a solution that replicates your JS purely using Django template tags
<div id="stats">
{% with ''|center:99 as range %}
{% for _ in range %}
<p>level {{ forloop.counter }}: hp {{ unit.hp_current }}</p>
{% endfor %}
{% endwith %}
</div>
As mentioned by #madhusudhan you can perform addition using filters
{{ unit.hp_current|add:forloop.counter }}

Using setattr() to update an object instance

I have a model class which has an attribute that refers to django DB objects. I would like to change this attribute using a single view with setattr() which I use to make changes to any attributes for this object.
The problem is, I can't seem to pass an object instance through the stack. I'm not sure if I can even use setattr() for this. Actually I'm not even sure if the problem is with my attempted use of setattr() or something else - please let me know!
Error on POST attempt:
ValueError at /dollhouseupdate/1
Cannot assign "u'Citadel'": "Dollhouse.dh_background" must be a "Background" instance.
Model:
class Dollhouse(models.Model):
dollhouse_name = models.CharField(max_length=100)
user = models.ForeignKey(User)
dh_background = models.ForeignKey(Background)
def __str__(self):
return self.dollhouse_name
Template:
<select id="background-select">
<option value="null">Change Background</option>
{% for background in background_objects %}
<option value="{{ background }}">{{ background.bg_name }} </option>
{% endfor %}
</select>
View:
def dollhouseupdate(request, dollhouseid):
if request.method == 'POST':
workingdollhouse = Dollhouse.objects.get(id=dollhouseid)
if request.POST.get('erase') == "true":
workingdollhouse.delete()
return HttpResponse("Dollhouse deleted!")
else:
data = (request.POST).dict()
for key, value in data.items():
setattr(workingdollhouse, key, value)
workingdollhouse.save()
return HttpResponse("Dollhouse {} saved!".format(workingdollhouse.dollhouse_name))
Javascript:
//change dollhouse background
$("#background-select").change(function() {
if($("#background-select").val() != null) {
var dollhouseid = workingDollhouse;
var dh_background = $("#background-select").val()
console.log("changing background to " + dh_background);
$.ajax("http://127.0.0.1:8000/dollhouseupdate/"+dollhouseid, {
type: 'POST',
data: {
dh_background: dh_background,
}
})
.done(function(response){
console.log("The request is complete!");
console.log(response);
window.location = "http://127.0.0.1:8000/";
})
.fail(function() {
console.log("Sorry, there was a problem!");
})
};
});
You are passing the object id in the POST variable, not the actual object itself (you can't do it anyway). Either change the following part
data: {
dh_background: dh_background,
}
to
data: {
dh_background_id: dh_background,
}
or get the object instance using the id in your view code.
As the error says, the Dollhouse.dh_background attribute must be an instance of the Background model. You are attempting to set its value to an object of a different type; I think a text string.
type(u'Citadel') is Background # False
Instead, you'll need to put some smarts into the view so that Background instances are retrieved by whatever key you have; then, set the Dollhouse.dh_background attribute to that instance.
if name == 'background':
background_code = post_args[name]
background = Background.objects.get(code=background_code)
workingdollhouse.dh_background = background
Because different POST arguments will refer to different fields, you will need to know what each one refers to and treat them differently. A simple “setattr for each one” won't work.

Not able to implement a dynamic dropdown list in Django

I need to implement two dropdown lists that the values of the seconds depends on the selection of the first.
I was able to implement that in the backend but I am struggling to do it in the front end and more specifically with javascript!
countries = Country.objects.filter(Enabled=True)
citiesByCountry = {}
for country in countries:
citiesInCountry = City.objects.filter(Enabled=True, Country=country)
cities = []
for city in citiesInCountry:
cities.append(city.Name)
citiesByCountry[country.Name] = cities
context = {'citiesByCountry': citiesByCountry}
return render(request, 'index.html', context)
So I have the following structure:
'Country':['City1', 'City2']
Here is the HTML:
<div class="form-group col-md-4">
<select class="form-control" onchange="test(this.value)" id="sel1">
{% for country in citiesByCountry %}
<option value="{{ country }}">{{ country }}</option>
{% endfor %}
</select>
</div>
<div class="form-group col-md-4">
<select class="form-control" id="cities">
</select>
</div>
So I have added the following javascript:
<script>
var country_objs = {};
{% for country, cities in citiesByCountry.items %}
country_objs['{{country|escapejs}}'] = '{{cities|escapejs}}';
{% endfor %}
</script>
<script type="application/javascript">
function test(country) {
var $cities_select = $("#cities");
$(country_objs[country]).each(function(){
$cities_select.append('<option>' + this + '<\option>');
});
}
</script>
The second dropdown never get populated but when I print the contents of the country_objs like this: console.log(country_objs[country]);
I get the following:
['City1', 'City2', 'City3']
Which is correct, but the .each function does not loop through the items. I think the problem is that the above is not a proper array but a string but still can't understand why.
Note that I get the following error:
jquery.min.js:2 Uncaught Error: Syntax error, unrecognized expression: ['City1', 'City2', 'City3']
Unfortunately whatever I try won't work, I couldn't imagine that implementing this in Django will be so hard.
I would like to avoid using a third-party app or module to do this simple thing and I would like to use a proper way to do it (i.e the best way) so any ideas will be really valuable.
There are two solutions:
Solution 1:
use a for loop:
country_objs['{{country|escapejs}}'] = [{% for city in cities %}"city",{% endfor %}];
Solution 2:
Switch the line:
citiesByCountry[country.Name] = cities
for:
citiesByCountry[country.Name] = json.dumps(cities)
to encode to json, and then in the template:
country_objs['{{country|escapejs}}'] = {{cities|safe}};
Obs regarding solution 2:
You can't have the single quotes around the variable
'{{cities|safe}}';
in the second solution, or else when you add the list ['City1', 'City2', 'City3'] you're gonna have:
'['City1', 'City2', 'City3']'
I think you want to remove the |escapejs filter for the part you want to be parsed in JavaScript. You might even find you need |safe, but you should be certain that you have control over what gets output there before considering that.
var country_objs = {};
{% for country, cities in citiesByCountry.items %}
country_objs['{{country|escapejs}}'] = {{cities|safe}};
{% endfor %}
For the updating part, this should work:
function updateCities(country) {
var $cities_select = $("#cities");
$(country_objs[country]).each(function(key, value) {
$('#cities').append($("<option></option>")
.attr("value",key)
.text(value));
});
}
$('#sel1').change(function() {
updateCities(this.value);
});
Credit due in part to this answer https://stackoverflow.com/a/171007/823020.
The above is missing an initial setting, which you could either do in templating or JavaScript. For JavaScript, you could insert another updateCities($('#cities).val());.
The above also appends every time, instead of resetting the options to an empty list. This is left as an exercise for the reader.
Suggestion 1: You didn't discuss this, but your initial query would be better done something like this:
# the following may differ based on your foreign key's related_name attribute
countries = Country.objects.filter(Enabled=True).select_related('city_set')
for country in countries:
citiesInCountry = country.city_set.values_list('name', flat=True)
This would all be a single query. However you'd need to rethink about the 'active' flag, or how to achieve that if you still need it.
Suggestion 2: To be honest, it might be better in general to wrap it up in json. In your view:
import json
countries_json = json.dumps(citiesByCountry)
and then in the template:
var country_objs = {{ citiesByCountry|safe }};

How to use the value of <select> in with django function?

So I want to make a function with if in my html using django.
My function looks like :
function try() {document.getElementById("demo").inerHTML = '{%for x in xl%}'+
'{%if x.name == document.getElementById("select1").value%}'+
'<button type="button">hello</button>'+
'{%endif%}{%endfor%}'}
And the relevant html is :
<select id="select1">
<option value = "tiger"></option>
<option value = "bear"></option>
<option value = "eagle"></option>
</select>
<p id="demo"></p>
<script>try();</script>
So just to make it clear :
'xl' is a list, the items in this list have a name variable.
I want my web to show in the "demo" paragraph 'hello' buttons for each time that the selcted animal is the name of item in the list.
When trying to run this it says : " Could not parse the remainder: '("select1").value;' from 'document.getElementById("select1").value;' "
Please help me find what`s wrong and how to fix it, Thanks.
Why not just use a Django form, and then get the selected option value with JavaScript? Example code has not been tested. You still need an app within your Django project, settings, templating, routing, etc.
# forms.py
class AnimalForm(forms.Form):
TIGER = 'Tiger';
BEAR = 'Bear'
EAGLE = 'Eagle'
ANIMAL_CHOICES = (
(TIGER, TIGER,),
(BEAR, BEAR,),
(EAGLE, EAGLE,),
)
animal = forms.CharField(max_length=20, choices=ANIMAL_CHOICES,
default=TIGER)
def save(self):
return NotImplementedError
# implement
# views.py
from django.shortcuts import render
from .forms import AnimalForm
def choose_animal(request):
form = AnimalForm(request.POST or None)
if request.method == 'POST' and form.is_valid():
form.save()
# do something, like redirect to a success page, etc
return render(request, 'animal_form.html',
{'form': form})
#animal_form.html
<form action="." method="post" enctype="application/x-www-form-urlencoded">
{{ form.animal }}
<button type="submit">Save</button>
</form>
<p id="animal-selection"></p>
<script>
// assuming jQuery is already linked for brevity
(function($) {
$(function() {
$('#id_animal').on('change', function() {
$('#animal-selection').html($(this).val());
});
});
})(jQuery);
</script>

Django: Referencing Views List in Java Script?

So I'm working with Django on a website that I am playing around with, and have been trying to research how I could get the following list in my views.py and be able to reference it in my javascript? I'm working on creating an ajax call and the tutorials I am coming accross are a bit confusing.
#lines 6 - 8 of my code.
def catalog_home(request):
item_list = item.objects.order_by('name') #item is the model name
note: the item model containts a name, description, overview and icon column.
Is it possible for me to use the list above (item_list) and be able to write a javascript function that does something similar to this? :
$(document).ready(function() {
$("#showmorebutton").click(function() {
$("table").append("<tr></tr>");
for (var i = 0; i < 3; i++) {
var itemdescription = item.description;
var itemName = item.name;
var icon = item.icon;
$("table tr:last").append(generateCard(itemName,
itemdescription,
icon));
}
function generateCard(itemNameC, itemdescriptionC, iconC) {
var card = "<td class='tablecells'><a class='tabletext' href='#'><span class='fa "
+ iconC
+ " concepticons'></span><h2 class='header'>"
+ itemNameC
+ "</h2><p>"
+ itemdescripionC
+ "<span class='fa fa-chevron-circle-right'></span></p></a></td>";
return card;
}
I don't mean to crowd source the answer to this, I just would appreciate any feedback/advice for me to handle this task, as I am fairly new to coding.
You should absolutely be able to do this. The trick is to understand Django templates. You showed part of the view but you will need to render to a template. Inside the template, you can just do something like
HTML code for page goes here (if you're mixing js and html)
<script>
var items = [{% for d in data %}
{% if forloop.last %}
{{ d }}
{% else %}
{{ d }},
{% endif %}
{% endfor %}];
// code that uses items
</script>
Note there's a little bit of work required to make sure you have the right # of commas [taken from this SO answer and your code should handle the case where the array is empty.
Alternatively, you could do an ajax call from static javascript to your django server using something like django rest framework to get the list of items as a json object.

Categories

Resources