Django : combine variable and HTML id in template - javascript

I'm looking for a way to "concatene" a django object list (get via the get_queryset in the views.py) with a Javascript variable. then concatene them to a field of a model object.
My model is a blog post, and contains the post_title field (a charfield).
In my index.html, I have a pager button with id="1" or id="2" corresponding to the index of the page and a onClick="displayPostContentOnPageButtonCliked(this.id)" attribute.
My index.html retrieve all post objects in a context_object_name called all_post_by_date.
In my index.html, I want to display on alert dialog the post title corresponding to the button clicked, something like:
<script>
function displayPostContentOnPageButtonCliked(page_button_clicked_id){
var all_posts_by_date = "{{all_posts_by_date.page_button_clicked_id.post_title}}";
alert(all_posts_by_date);
}
</script>
But this doesn't work. I tried to add filter or create my own filter (called get_index, but only manage to get like: all_posts_by_date|get_index:page_button_clicked_id and not be able to combine the .post_title part.
My question is: how to get {{all_posts_by_date.page_button_clicked_id.post_title}} to work, and alert "Title of post 3" when the button 3 is clicked ?
Thank u !

Two solutions for you: (1) send in the data you want to your JS function in the call to the JS function.
<script>
function showPostTitle(post_title){
alert(post_title);
}
</script>
. . .
{% for x in all_posts_by_date %}
<div id='blog-post-{{ x.id }}' on-click="showPostTitle('{{ x.post_title }}')">
. . .
{% endfor %}
Or you can just serialize the data yourself for later use:
<script>
var titles = [];
{% for x in all_posts_by_date %}
titles.push('{{ x.post_title }}');
{% endfor %}
function displayPostContentOnPageButtonCliked(nId) {
var title = titles[nId - 1];
alert(title);
}
</script>

Related

How to display django queryset values in templates using javascript

I am trying to display list of names from a django queryset using , Here is my code
def vfunc(request):
context={}
bats = Bat.objects.all().order_by('-id')
context['bats'] = bats
[each.batname for each in bats] # Gives all the batnames.
return render(request, 'bat.html', context)
I want to display batnames on template
$(function() {
//This works when I hardcode the values
var batData = [{
"bat":"batname1", //hardcoded & displayed in 1st row
}, {
"bat":"batname2", //hardcoded & displayed in 2nd row
}];
$("#CleanDatasNames").dxDataGrid({
dataSource: batData,
...
...
}
I am tried something like var batData = [{"bat":"{{bats|join:', ' }}"] but it is displaying obj1,obj2 in same row,I also tried to loop through but failed , any help will appreciated.
This way you can work with js:
$(function() {
//This works when I hardcode the values
var batData = [
{% for bat in bats %}
{
"bat":"{{bat}}",
}
{% if forloop.counter != bats|length %} // dynamically checking for last element
,
{% endif %}
{% endfor %}
];
$("#CleanDatasNames").dxDataGrid({
dataSource: batData,
...
...
}

Updating a twig var inside a function

I'm trying to work with twig and javascript. I'm just trying to define a var and update the value inside a click function. Unfortunatelly, the var seems to be updated, but the new value is not updated when the scope of the function ends, so the console log writes 1 the first time and 2 each time I enter in the click function.
Which is the better way to do this?? Thanks in advance
{% set actual_index = 0 %}
{% set num_elements = 3 %}
addNewEvents();
{% set actual_index = actual_index + 1 %}
console.log({{ actual_index }})
$("#load").click(function () {
addNewEvents();
{% set actual_index = actual_index + 1 %}
console.log({{ actual_index }})
});
How to do it cleanly? Separate twig from your javascript. They were not intended to be used together (even though it can work).
You have multiple different ways to retrieve the information for your js. Either use twig and store the information in a hidden input, or get the information with an AJAX call.
for example:
var actual_index = -1;
var num_elements = 0;
$.getJSON('ur_here', function(data){
actual_index = data.actual_index;
num_elements = data.num_elements;
});
But it seems to go further than that, you understand that twig generates HTML server side and sends it to the client? This happens only once.
That means your variable you set only exists server side right? That means that when you do {{ actual_index }} it just generates a number client side and not a variable.
{% set actual_index = 0 %}
{% set num_elements = 3 %}
The two above variables exist server side right before twig generates your HTML and sends it to client.
Here is what your js should look like without twig:
var actual_index = 0;
var num_elements = 3;
addNewEvents();
actual_index = actual_index + 1;
console.log(actual_index)
$("#load").click(function () {
addNewEvents();
actual_index = actual_index + 1;
console.log(actual_index);
});
EDIT:
Answering your comment.
"{{ asset( path('event_values', { 'programmerid': programmerid, 'actual_index': actual_index, 'num_elements':num_elements } ))|raw }}
If the above generates an URL then you're on the right path (otherwise check this post How Generating URLs from a template correctly in Symfony2/Twig ).
Now, the first parameter of the getJSON is an URL, and the second parameter is the callback.
Remember that your {{ asset... }} should generate an URL that can be called on the client side.
So after your twig generates your HTML the URL should look something like this:
$.getJSON('http//:localhost/path/to/your/ajaxHandler.php', function(data){
//this is called when you return data from your ajaxHandler.php
//do something with data
});
This way, when you click ajaxHanlder.php is called and you can send back the information back in json ( echo json_encode(You array here); )

Django-autocomplete-light how to get data from html?

I can't get how to fetch data from HTML-element that contains data generated by django-autocomplete-light.
Here is a code of the form:
class ThreadForm(forms.Form):
topic = forms.CharField(label="Topic", max_length=255)
body = forms.CharField(label="Body", widget=forms.Textarea(attrs={'rows': '12', 'cols':'100'}))
tags = autocomplete_light.fields.MultipleChoiceField(choices=(tuple((tag.name, tag.name) for tag in Tag.objects.all())),
label='Tags',
widget=autocomplete_light.widgets.MultipleChoiceWidget('TagAutocomplete',
attrs={'class':'form-control',
'placeholder':'Tag'}
)
)
def save(self, author, created):
topic = self.cleaned_data['topic']
body = self.cleaned_data['body']
tags = self.cleaned_data['tags']
th = Thread(author = author,
topic = topic,
body = body,
created = created,
)
rtags = []
for tag in tags:
sr = Tag.objects.get(tag)
rtags.append(sr.name)
th.save()
Tag.objects.update_tags(th, tags)
And autocomplete_light_registry.py:
from threads.models import Thread
import autocomplete_light
from tagging.models import Tag
class TagAutocomplete(autocomplete_light.AutocompleteModelBase):
search_fields = ['^name']
autocomplete_light.register(Tag, TagAutocomplete, attrs={
'data-autocomplete-minimum-characters': 1,
},)
As you see I've changed the django-autocomplete app. In the base.py I found a variable choice_html_format = '<span data-value="%s" name="choice">%s</span>'
Attribute name was added by me to get data like that:
tags = request.POST.get('name')
But this doesn't work. I'm getting an error like "NoneType in not callable"
Next thing I've tried is change choice_html from base.py:
def choice_html(self, choice):
"""
Format a choice using :py:attr:`choice_html_format`.
"""
return self.choice_html_format % (
escape(self.choice_value(choice)),
escape(self.choice_label(choice)))
It is original function, I've changed choice_value(choice) to choice_label(choice). And got an error "invalid literal for int() with base 10: <tag_name_here>". Looks like data-value attribute is only for int() type (but I can't get where I can change it, maybe in js-function, I don't know).
And the last, I'm trying to get the pk of each tag, and then get the name via manager. But I'm getting error Cannot resolve keyword '4' into field. Choices are: id, items, name.
I absolutely sure that there is an easy way to perform the task I need.
autocomplete-light has a template called widget.html that is rendered in the template:
...
{% block select %}
{# a hidden select, that contains the actual selected values #}
<select style="display:none" class="value-select" name="{{ name }}" id="{{ widget.html_id }}" multiple="multiple">
{% for value in values %}
<option value="{{ value|unlocalize }}" selected="selected">{{ value }}</option>
{% endfor %}
</select>
{% endblock %}
...
as you can see, this <select> element contains all selected choices for the autocomplete widget.
Its name (we are going to identify it by its name attribute later in the view) is simply the autocomplete's name ('tags').
So now you need to make sure your autocomplete field in the template is wrapped in <form> tags so the values get submitted (if you haven't already).
The next step is to retrieve the data in the view:
request.POST.getlist('tags')
That's it. You now have a list of primary keys of the selected values:
>>> print(str(request.POST.getlist('tags'))
['1', '3', '4', '7', ...]

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.

New forms - embedding a collection of forms - Symfony2

I'm doing the same as explained here: http://symfony.com/doc/current/cookbook/form/form_collections.html
But in my case I want to add new "tags" not manually with clicking on a link, but automatically. I give to my template an array with items and for each of this items I want to add a new form - the number of items should be equal to the number of forms.
If it's possible, I'd prefer a solution like this:
{% for i in items %}
{{ i.name }} {{ form_widget(form.tags[loop.index0].name) }}
{% endfor %}
But how to automatically create objects in the controller, too? It tells me that there is no obeject with index=1, and yes - there isn't, but isn't there a way to create them automatically and not need to create for example 10 empty objects of the same kind in my controller? :(
Another thing I was thinking was something like this:
{% for i in items %}
<ul class="orders" data-prototype="{{ form_widget(form.orders.vars.prototype)|e }}">
{{ i.name }} and here should be a field from the form, for example tag.name
</ul>
{% endfor %}
I suggest that the js given in the cookbook should be changed to do this, but I'm not good in js and my tries didn't do the job.
I tried putting this in the loop:
<script>
addTagForm(collectionHolder);
</script>
and this in a .js file:
var collectionHolder = $('ul.orders');
jQuery(document).ready(function() {
collectionHolder.data('index', collectionHolder.find(':input').length);
function addTagForm(collectionHolder) {
var prototype = collectionHolder.data('prototype');
var index = collectionHolder.data('index');
var newForm = prototype.replace(/__name__/g, index);
collectionHolder.data('index', index + 1);
var $newFormLi = $('<li></li>').append(newForm);
}
});
Assuming that your main class has addTag($tag) method, you can add different 'new' tags to it.
In class Task
public function addTag($tag){
$this->tags[]=$tag;
return $this;
}
In your Controller (assuming 10 tags here)
$task=new Task();
for($i=0;i<10;i++){
$task->addTag(new Tag());
}
$form->setData($task);
In your view
{% for tag in form.tags %}
<ul class="orders">
<li>{{ form_widget(tag.name) }}</li>
</ul>
{% endfor %}
If you don't need the manually click, you can remove the JavaScript part.

Categories

Resources