Append Django template tag with Jquery - javascript

I want to build a menu where I can set one link highlighted using the {% block %} tag. I have something like this in my Javascript:
<loop>
$('#a-div').append('{% block ' + variable + ' %} <a href...</a> {% endblock %}')
<endloop>
In the source, this is displayed as "{% block Home %}"
How can I make JQuery not append this as a string but as a template tag?

You can't. At least not without making an AJAX request to a Django template. In your case, it would be slow and make unnecessary extra requests. It's just not worth it. You can insert snippets from Django templates via jQuery by using, for example, the jQuery load function. But you can't replace a specific {% block %} tag, because by the time jQuery runs, the template has already been processed (and references to block tags removed). But this is not a situation where you should be doing that in any case.
Why don't you rather highlight the menu with a CSS class? Here is my usual solution to this problem:
Create a file called base_extras.py in one of your templatetags folders. If you don't have one, create one in an appropriate folder.
Inside base_extras.py, paste this code:
from django import template
from django.core.urlresolvers import reverse
register = template.Library()
#register.simple_tag
def navactive(request, urls):
if request.path in ( reverse(url) for url in urls.split() ):
return "active"
return ""
Now, in your template, on your menus in your base template, do something like this:
<ul class="menu">
<li class="home {% navactive request 'home' %}">Home</li>
<li class="contact {% navactive request 'contact' %}">Contact</li>
<li class="signup {% navactive request 'signup' %}">Sign up</li>
</ul>
This will make that the menu where your URL currently is has the active class. Then, in your CSS, just add a special class for a menu item with active to look slightly different than the other menus.
ul.menu li.active {background: red; color: white;}
And if you happen to need to change the active menu with jQuery, you can just remove the active class on all menus, and add it to the newly selected menus:
$('ul.menu li').removeClass('active').find('.home').addClass('active'); // for example

You can't do that like that. The Django template tags are processed on the server side, before the page is even sent to the browser. Javascript (including jQuery) is, on the other hand, invoked in the browser, after the page has been received from the server.
What you can do is prerender the content of {% block %} tag to JS variable and use it in jQuery code:
var blockContent = "{% block Home %} ... {% endblock %}";
// ...
$("#a-div").append(blockContent);
If you need more than one block to choose from (as you seem to indicate in the code sample you've provided), you could resort to an array of prerendered blocks.

Your best bet is to create a proxy view that makes what is currently your AJAX request, processes the results like the javascript would, and then returns whatever you're trying to get from the Django templating system.
Then, instead of making the AJAX call you're currently making, you call your own view instead. Django does the processing in the view like it should, you get fine-grained control over what's returned to your javascript, and it's still only one (client-side) server call.

Related

Django and HTMX I am looking for not "hacky" way to update class of link that calls HTMX request. Like "refresh-self" or something along those lines

I have a problem with HTMX in Django. I basically have two important components on page. List of categories and content that is being shown after you click on category.
I was working nicely with just standard htmx "out of the box". But I started having problems when I wanted to add active css class on category link after you click it (to show user where he is currently).
I did a lot of experiments with hx-swap-oob and hx-swap but the only thing that work was this:
(it is the most relevant part of the code)
<div class="col-sm-4">
<div class="card">
<div class="card-body" hx-boost="true" hx-target="#manual_results">
<div id="manual_categories">
{% include 'partials/manual_categories.html' %}
</div>
</div>
</div>
</div>
<div class="col-sm-8">
<div id="manual_results">
{% include 'partials/manual_entries_list.html' %}
</div>
</div>
and in manual_entries_list.html:
<some html results>
<div id="manual_categories" hx-swap-oob="true">
{% include 'partials/manual_categories.html' %}
</div>
Each category has simple if statement in django template code that is checking if it is selected (based on url path.)
And it is working, thing is, on the first request the categories are rendered twice (which is logical since I have 2 includes on the same HTML). After I select one category, everything goes back to normal because HTMX "starts to understand what is happening" and it switches the categories from manual_entries_list.html into our main page.
And like I said it works, I modified manual_entries_list.html to:
<some html results>
<div class="set_size_to_0px">
<div id="manual_categories" hx-swap-oob="true">
{% include 'partials/manual_categories.html' %}
</div>
</div>
So it is always invisible (so I will have only one set of categories visible).
The thing is, that it is an obvious hack. I am sure that there needs to be a better way of solving this problem but I cannot find it.
(I even tried to do it with plain javascript the thing is that categories are rendered in a for loop so it is pretty much impossible to get IDs correctly etc.)
Could someone please help me?
The easiest way to avoid this issue is to detect the HTMX request in the view function, pass this state to your templates and render HTMX content only if needed. HTMX will add a HX-Request: true header to each request.
For the detection you can use the Django-HTMX package that provides a request.htmx variable in your view functions that will be True if the request is coming from HTMX. Or if you want to check it manually:
def my_view(request):
is_htmx = request.headers.get('HX-Request') == 'true'
return render(request, 'my_template.html', {'is_htmx': is_htmx})
After that in manual_entries_list.html template include HTMX related stuff only in the HTMX requests:
<some html results>
{% if is_htmx %}
<div id="manual_categories" hx-swap-oob="true">
{% include 'partials/manual_categories.html' %}
</div>
{% endif %}

$(...).sortable is not a function when in JQuery callback

With this HTML (twig) :
<li class="ipsFieldRow">
<ul id="customSortable">
{% for plugin in plugins %}
<li id="{{plugin.name ~ '-sort'}}">{{plugin.name}}</li>
{% endfor %}
</ul>
<script>
//Put Here Works Fine
$("#customSortable").sortable();
$("#customSortable").disableSelection();
//Put in a document ready causes the '$(...).sortable is not a function' error.
$(document).ready(function() {
$("#customSortable").sortable();
$("#customSortable").disableSelection();
});
</script>
<li
The problem is described through the two comments in the code. Simply put, .sortable works perfectly when simply inlined into the HTML yet in any jquery callback, it results in the sortable is not a function error. The main reason why this is an issue for me is I want to call sortable('toArray') in a button click callback for server interaction.
As seen through the image, the common issue of the includes being in the wrong order is not the issue here.
You should wrap your code in an IIFE to give yourself an isolated scope.
You can then save a local reference to your jQuery instance, so you'll be unaffected by future changes to the global window.$.
If you installed jQuery yourself (as opposed to relying on an existing copy), you may also want to call .noConflict().

Symfony2 is refreshing a twigs block with ajax possible?

Lets say I have a block in my layout:
{% block sidebar %} {% render url( 'sidebar' ) %} {% endblock %}
Is it possible to refresh the block with ajax without making a div around it? In my example I cant make a div, because it crashes my whole template so I need to know is that even possible?
For example I can refresh a div like this(.test is the class of the table):
$('.test').load(" .test");
Can I make something like this to refresh the block?
$('sidebar').load(" sidebar");
Any ideas?
Symfony works server side so it can't know what happens in your DOM once the page has been rendered. Also jquery can't know about twig blocks since those are parsed server side.
I'm gona give you a "stupid" (and probably i'm even going against good practices, depending on the content you are rendering) answer but maybe it can be of help: have you tried putting a "span" around it instead of a div?
{% block sidebar %} <span class="test">{% render url( 'sidebar' ) %}</span> {% endblock %}
EDIT:
I think an explanation is due:
This answer is correct assuming there aren't divs inside your sidebar, otherwise it's just a "cheap trick" and might cause other issues (if not now, maybe in the future) See div inside the span element for example.
Probably you should need to check your layout if adding div screws it up.
An alternative i suggest you to try if it can work in your case is to use inline-block divs.
{% block sidebar %} <div class="test">{% render url( 'sidebar' ) %}</div>{% endblock %}
Then, in your css:
.test {
display: inline-block;
}
See http://learnlayout.com/inline-block.html (and remember it's not entirely supported by IE6 and 7)
No. It is not possible.
The reason behind this is separations of web tiers i.e. jQuery runs on Client end which TWIG on Server.

Appended content disappears after the change of URL

On my webpage I am using Flask microframework. To give you better understanding of my problem I have decided to include several of my files. Hopefully I won't discourage you.
Example bare in mind this example is incomplete due to complexity of my website. But I hope it will ilustrate the problem.
My script.py which runs the server:
from flask import Flask, request, render_template
app = Flask(__name__)
#app.route('/')
#app.route('/<name>')
def home(name=''):
return render_template('home.html', name=name)
#app.route('/user/')
#app.route('/user/<task>')
def user(task=''):
return render_template('user.html', task=task)
Then I have template.html:
<!-- some html code -->
<div id="navBar"></div>
<div id="mainContent">
{% block content %}
{% endblock %}
</div>
<!-- some html code -->
and user.html:
{% extends 'template.html' %}
{% block content %}
{% if task == 'homework' %}
{% include '/tasks/homework.html' %}
{% endif %}
{% endblock %}
And finally my script.js:
// some jQuery script
$('#button').click(function(){
$('#navTask').empty();
$('#navTask').append('\
<div class="task">\
Homework\
</div>');
});
// some jQuery script
Back to my problem! Basically it does this. Once you click the #button it appends #navTask (which is div located somewhere on the page) with some div with link to /user/homework. Once you click on Homework Flask recognises the change of URL and renders user.html so some part of the webpage changes. My problem is: once you click the link Homework the link Homework disappears too. I am not sure, how to fix it.
I thought, that by appending the div.task to #navBar it will changes throughout the webpage no matter the URL. But as far as I can tell it does not.
I am hoping for this behaviour - somewhere on the page there is a div, after clicking #button, the content changes and new link appears. When you click the link the content somewhere on the page changes, but the link stays where it was. The link might disappear e.g. only when you are on home page.
Note I am now quite sure, if my expectations are real. I could probably do this with just using jQuery, but because the main.py is much more complicated and I am using the data obtained from URL I still go this "way".
Maybe I misunderstood you, but you need to use AJAX request triggered from click on the link if you don't want to refresh all page content.
Whenever was committed not Ajax request to server - the page will be completely redrawn.
On the server side you could check whether it XMLHTTPRequest and if it is return JSON data or other convenient for you.
Maybe this little flask-jquery-example will help you.
P.S. Current location on JS side you can get by window.location.pathname or $(location).attr('href')

Javascript / Django Design Pattern

I just have a question about how to achieve DRY with javascript that generates html on the fly. I have a list of elements that are loaded dynamically and populated by the django template a la
{{ tag.title }}
{% if request.user.is_authenticated %}
<a name="del-tag" data-id="{{ tag.id }}" class = "tag-x" title="Remove Tag" href="#">x</a>
{% endif %}
Now, I have some javascript that also loads new tags via ajax. Here's the relevant portion:
var newTag = "<span class = \"tag\">" + tagName + "<a name=\"del-tag\" data-id=\"" + tag_id + "\"" +
"class = \"tag-x\" title=\"Remove Tag\" href=\"#\">x</a></span>";
$('#tags').append(newTag);
Can I avoid duplicating HTML in the javascript?
Thanks.
jQuery Template could be used for this.
jquery(I'm assuming that's jquery that you are using) has a clone feature that can clone DOM elements. Given that you should be able to clone one of the html elements that already exist and change the value of the attributes, and then append it back to the DOM. I have not done this myself but it should work in theory.
Yes, you can do this. Have all tags generation functionality in a separate template. Then have some url which generates tags. Like this:
www.example.com/tags/?tags=tag1,tag2,tag3
this produces:
Then, when doing the AJAX call in your code do something like this:
$('div.tags').load('www.example.com/tags/?tags=tag1,tag2,tag3')
On the Django/template side you'd want to find a way how to include the result returned by the URL into the page template body. I'm not exactly sure what tools Django template engine provides, but on the first view it looks like you could put this code into some view method and extend this view everywhere you need it, providing the template variable as following render(..., tags=self.generate_tags(args)), in template it would be just {{ tags }}.
Both /tags/?tags=... and regular page /page/calls could re-use the generate_tags() method then.
Hope it helps

Categories

Resources