Why htmx trigger only work once in django - django

I am using htmx to trigger a field in Django ModelForm with the following codes.
Everything works as it supposed to the first time around, but after that when you change the option select field nothing happen, no trigger whatsoever. I have to reset and go back to url 'listing' for it to respond again. I want the code to trigger the result everytime I change the option select field before I finally submit. Any help is well appreciated.
class Listing(model.Model):
option=models.ForeignKey(Option,on_delete=models.CASCADE)
package=models.ForeignKey(Package,on_delete=models.CASCADE,blank=True,null=True)
number=models.ForeignKey(Number,on_delete=models.CASCADE,blank=True,null=True)
period=models.ForeignKey(Period,on_delete=models.CASCADE,blank=True,null=True)
title=models.CharField(max_length=20)
class ListingForm(ModelForm):
class Meta:
model=Listing
fields='__all__'
class ListingCreateView(CreateView):
model=Listing
form_class=ListingForm
template_name='listing_form.html'
success_url='/forms/listing/'
def option(request):
option=request.GET.get('option')
form=ListingForm
context={'option':option,'form':form}
return render(request,'partial_option.html',context)
urlpatterns=[
path('',ListingCreateView.as_view(),name='listing-create'),
path('option/',option,name='option'),
]
listing_form.html
{% load widget_tweaks %}
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/htmx.org#1.6.1"></script>
</head>
<body>
<h1>Listing Form</h1>
<form method="post">
{% csrf_token %}
<div>
{{ form.option.label_tag }}
{% render_field form.option hx-get="/forms/option"
hx-trigger="change" hx-target="#option" hx-swap="outerHTML" %}
</div>
<div id="option"></div>
<input type="submit" value="Send">
</form>
<script>
document.body.addEventListener('htmx:configRequest', (event) =>
{
event.detail.headers['X-CSRFToken']='{{csrf_token}}';
})
</script>
</body>
</html>
partial_option.html:
{% if option %}
{% if option =='1' %}
<p>You have chosen option 1</p>
{% elif option == '2' %}
<p>You have chosen option 2</p>
{{ form.package.label_tag }}
{{ form.package }}
{% elif option == '3' %}
<p>You have chosen option 3</p>
{{ form.number.label_tag }}
{{ form.number }}
{{form.period.label_tag }}
{{ form.period }}
{% endif %}
{% else %}
<p>You have no option</p>
{% endif %}
{{ form.title.label_tag }}
{{ form.title }}

You have set the hx-swap="outerHTML" method, so HTMX will replace the target element with the response. Since your response does not contain a new <div id="option"> element, after the first request/swap cycle HTMX cannot find the target.
To solve this issue, change the swap method to innerHTML or embed the response in a <div id="option"></div> element.

Related

ckeditor not saving changes django

I have a form where in one of the fields, I use the ckeditor. However when I submit the form, the changes in the ckeditor field is not being saved. In the model, I have changed the field to aRichTextField. I have installed "ckeditor" in my apps in settings as well.
I have also both tried to load these scripts in my template:
{% load static %}
<script type="text/javascript" src="{% static "ckeditor/ckeditor-init.js" %}"></script>
<script type="text/javascript" src="{% static "ckeditor/ckeditor/ckeditor.js" %}"></script>
On top of that have I also tried to add the {{ form.media }} instead of the scripts but it does still not work.
I am using HTMX to dynamically update the form.
This is my form template right now
<form action='' method="POST" class="form" hx-post='' hx-swap='outerHTML'>
{% csrf_token %}
{{ form.media }}
<div class="form-group">
{% for field in form %}
{{ field }}
</div>
{% endfor %}
<br>
<div class='htmx-indicator'>Loading...</div>
<div class="text-center">
<button class='htmx-inverted-indicator' type='submit' >Save</button>
</div>
{% if message %}
<p>{{ message }}</p>
{% endif %}
</form>
Does anybody know why the form is not being saved?
EDIT
This is my view
#login_required
def book_update_view(request, id=None):
book = get_object_or_404(Book, id=id)
form = BookForm(request.POST or None, instance=book)
context = {
"form": form,
"object": book,
}
if form.is_valid():
form.save()
context['message'] = 'Saved!'
if request.htmx:
return render(request, "book/snippets/forms.html", context)
return render(request, "book/update.html", context)
Looks like there is a conflict between the CKEditor and HTMX. The below relies heavily on this answer. It makes the following changes:
Switches the HTMX labels to the button rather than the form
Applies an event
listener to the CKEditor - it does this via the {{field.label_tag}}
which is now included
Fixes up a misplaced tag
Try making your form something like this (don't forget to replace the name of the CKEditor field - you may need to check your source code to see how this is rendered):
<form method="post">
{% csrf_token %}
{{ form.media }}
<script>
document.body.addEventListener('htmx:configRequest', (event) => {
var element = new CKEDITOR.dom.element( document.getElementById( '{{ form.NAMEOFCKEDITORFIELD.id_for_label }}' ) );
event.detail.parameters['{{ form.NAMEOFCKEDITORFIELD.html_name }}'] = element.getEditor().getData();
})
</script>
<div class="form-group">
{% for field in form %}
{{ field.label_tag }}:<br />{{ field }}
{% endfor %}
</div>
<br>
<div class='htmx-indicator'>Loading...</div>
<div class="text-center">
<button class='htmx-inverted-indicator' type='submit' hx-post="{% url 'book_update_view_name' book.id %}" hx-target="#{{form.id}}" hx-swap="outerHTML">Save</button>
</div>
{% if message %}
<p>{{ message }}</p>
{% endif %}
if you dont want extra get parameter which might give you a problem, you can put onclick on your submit button to have ckeditor transfer the data to your field. something like this :
<script>
function saveCK(){
let ckdata = CKEDITOR.instances.your_field_id.getData();
$('#your_field_id').val(ckdata);
}
</script>
yeah, sorry for the jquery, I'm not sure vanilla javascript equivalent.
and on your submit button, add
onclick="saveCK()"
One more option is to use hx-vals.
For example:
<button type='submit' hx-vals="js:{ {{ form.NAMEOFCKEDITORFIELD.name }}: CKEDITOR.instances['{{ form.NAMEOFCKEDITORFIELD.id_for_label }}'].getData()}" hx-post=POSTURL>Save</button>

Show / hide input field or div based on RadioField choice using Flask wtf_form

I am trying to build website using Flask, and can't find solution to one problem. I am trying to hide or show input field, based on RadioFeild choice. Also, if input field showing, it have to be required. I actually have everything working, besides knowing what RadioField choice was selected. I can see the form when loading page on 127.0.0.1:5000 and I can see RadioField with 2 choices: Yes and No, but when I click on yes, there are no changes, hidden input field is still not showing. Please, help!
app.py
class MyClass(FlaskForm):
my_field = RadioField("bla-bla-bla", choices=[('Yes', 'Yes'), ('No', 'No')], validators [InputRequired()])
#app.route("/some_page")
def some_page():
form = MyClass()
return render_template("some_page.html", form=form)
_render_field.html
{% macro render_radio_field(field) %}
<div class="form__item">
<label class="form__label">{{field.label.text }}</label>
<div class="form-group">
{{ field(class_='form__input', **kwargs)|safe }}
{% for subfield in field %}
<div class="form__item">
<label>
{{ subfield }}
{{ subfield.label.text }}
</label>
</div>
{% endfor %}
</div>
</div>
{% endmacro %}
some_page.html
{% from "_render_field.html" import render_field, render_radio_field %}
{% extends "layout.html" %}
{% block title %}My Title{% endblock %}
{% block content %}
<div style="width:600px; margin:0 auto;">
<h3>Some Text</h3>
<form class="form" action="{{url_for('some_page')}}" method="POST">
{% from "_render_field.html" import render_field, render_radio_field %}
{{ form.csrf_token }}
{{ render_field(my_field, title="", style="list-style:none") }}
{% if form.my_field.option == "Yes" %}
{{ render_field(form.some_other_StringField, placeholder="Please explain:", title="") }}
{% endif %}
<input type="submit" name="" value="login" class="form__btn">
</form>
</div>
{% endblock %}

Why is this django formset not being submitted?

i have a formset as follows:
EduFormSet = formset_factory(forms.CandidateDegreeForm, can_delete=True)
edu_formset = EduFormSet(prefix='candidate_degree')
in the templates i am doing the following:
{% if edu_formset %}
{% for form in edu_formset %}
<div class="formset-form" style="visibility: visible;">
<form id="{{ form.prefix }}" method="POST" action="/degree/add/">
<h4>Some Heading Here</h4>
{% csrf_token %}
{% for field in form %}
{% include "form_field.html" %}
{% endfor %}
</form>
<script type="text/javascript">
jQuery(document).ready ( function(){
jQuery('{{ form.prefix }}').validationEngine();
});
</script>
<div class="clearfix"></div>
</div>
{% endfor %}
{{ edu_formset.management_form }}
<div class="button-container right">
<input class="button" type="submit" value="submit" />
</div>
{% endif %}
I am not sure why but nothing really happens when i hit the submit button.
Your submit button is not within the form, so the action is not triggered by the click!
Here's how the docs show you to render formsets:
<form method="post" action="">
<!-- Notice how the formset (below) and thus its submit button
is INSIDE the form (above) -->
{{ formset.management_form }}
<table>
{% for form in formset %}
{{ form }}
{% endfor %}
</table>
</form>
You try to create multiple forms with the form.prefix for id. This could work but each form would have to be rendered with its own submit button. Formsets are designed to combine multiple forms into one and guarantee uniqueness of value names by said prefix. They would be enclosed in a singe form and share any submit triggers.

How to use array elements as labels using for loop in Template Django?

I have the following template code,presently i m getting for loop counter as label for my formset. How can i get elements of array 'month' (eg month.counter, where counter is loop) as my label?? I tried {{month.forloop.counter}} but that didnt worked
<html>
<head>
<title>Actuals</title>
</head>
<body>
<h1>Actuals Data</h1>
<h2>Your Account Number is : {{ Account_Number }}</h2>
<h2>You Chose {{ Year }} {{month}} as period.</h2>
{% if form.errors %}
<p style="color: red;">
Please correct the error{{ form.errors|pluralize }}below.</p>
{% endif %}
<form action="." >
{{ formset.management_form }}
<table>
{% for form in formset %}
{{form.id}}
<div class="field">
{{ form.Value.errors }}
<label for="id_Value">{{months}}.{{forloop.counter}}</label>
{{ form.Value }}
</div>
{% endfor %}
</table>
</form>
</body>
</html>
You can do this using custom templatetags. Sample code is given below:
add following to /{app_name}/templatetags/app_tags.py
from django import template
register = template.Library()
#register.filter
def month(value, counter):
try:
month = value[counter]
except IndexError:
month = ""
return month
put following in your template
{% load app_tags %}
............
............
{% for form in formset %}
{{form.id}}
<div class="field">
{{ form.Value.errors }}
<label for="id_Value">{{ months|counter:forloop.counter }}</label>
{{ form.Value }}
</div>
{% endfor %}
............
............
View this link, some person has also tried different methods to do this; although none of them worked. ;)
There isn't ready-made filter/tag for this in django template.
You could try writing your custom filter/tag. Refer Custom template tags and filters

User settings in django admin

I want to be able to change some settings from django admin, for example: site title or footer. I want to have app with model which includes this settings, but this settings should be in single copy. What the best way to do it?
You can create a view with #staff_member_required decorator, which renders/saves a form:
from django.contrib.admin.views.decorators import staff_member_required
...
#staff_member_required
def edit_config(request, ):
saved = False
if request.method == "POST":
form = ConfigForm(request.POST)
if form.is_valid():
...
# Do saving here
saved = True
else:
form = ConfigForm()
...
context = {
'form': form,
'saved': saved,
}
return render_to_response('staff/edit_config.html', context, context_instance=RequestContext(request))
Use django forms in the view, and pass it to the template.
then, in the template extend 'admin/base_site.html' so your form has a admin look and feel. Here's a sample template:
{% extends 'admin/base_site.html' %}
{% load i18n adminmedia %}
{% block title %}Edit Configuration {{ block.super }} {% endblock %}
{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% admin_media_prefix %}css/forms.css" />{% endblock %}
{% block breadcrumbs %}
<div class="breadcrumbs">
{% trans "Home" %} > Edit Configuration
</div>
{% endblock %}
{% block content %}
<h1>Edit Configuration</h1>
{% if saved %}
<p class="success" style="background-color:#9F9; padding: 10px; border: 1px dotted #999;">
Settings were saved successfully!
</p>
{% endif %}
<form method="POST" action="">
{% csrf_token %}
<fieldset class="module aligned">
<h2>Configuration</h2>
<div class="description"></div>
{% for field in form %}
<div class="form-row {% if field.errors %}errors{% endif %}">
{{ field.errors }}
<div class="field-box">
{{ field.label }} : {{ field }}
{% if field.help_text %}
<p class="help">{{ field.help_text|safe }}</p>
{% endif %}
</div>
</div>
{% endfor %}
</fieldset>
<div class="submit-row">
<input type="submit" value="{% trans 'Save' %}" class="default" name="_save"/>
</div>
</form>
{% endblock %}
You can use database, ini files, redis, ... for storing your configuration. You may define some general backend, and inherit your custom backends from it so it's flexible.
Sounds like django-constance would be a good fit.
Though django-flatblocks might be sufficient.
django-flatblocks is a simple application for handling small
text-blocks on websites. Think about it like django.contrib.flatpages
just not for a whole page but for only parts of it, like an
information text describing what you can do on a site.