HTMX not triggering correct query on pickadate.js selection - django

I use django-forms-dynamic package and htmx to dynamically load available options on a MultipleChoiceField. The options are based on a date field, for which I use pickadate.js by Amsul.
The initial query gets the correct choices from the database. However, if the date is changed, the query is lagging one step behind. So, let's asume 1.11.2022 is initially selected. If changed to 4.11.2022, the query is made for the 1.11.2022. If 28.11.2022 is selected, 1.11.2022 is queried, etc.
reservation_form.html
<div class="col-lg-6">
<div class="form-floating">
{% render_field reservation_form.date class="datepicker form-control mb-3"
hx-get="/reservation/filter-seats"
hx-include="#id_dinner"
hx-trigger="click change"
hx-target="#id_seat_reservation"
%}
<label for="id_date">Dinner Date</label>
</div>
<div class="form-floating">
{% render_field reservation_form.amount_guests class+="form-control" placeholder="" %}
<label for="id_amount_guests">Guests</label>
</div>
<div class="visually-hidden">
{% render_field reservation_form.dinner %}
</div>
<div class="form-check">
{% render_field reservation_form.seat_reservation class+="form-select" %}
<label for="id_seat_reservation">Select Seats</label>
</div>
</div>
pickadate script
<script>
var $input = $('.datepicker').pickadate({
format: 'yyyy-mm-dd',
formatSubmit: 'yyyy-mm-dd',
min: 0,
max: 90,
disable: {{ blocked_dates }},
firstDay: 1,
})
var picker = $input.pickadate('picker')
</script>
What am I missing?

You have set the triggers as hx-trigger="click change". First, this is incorrect, you have to separate events with a comma, right now HTMX tries to evaluate change as a trigger modifier without success. Therefore you only have the click trigger event, HTMX submits the form when you click on the input element. At the same time pickadate.js also listens to the click event and opens the datepicker widget. After selecting a date the form dispatches the change event, but you have disabled that for HTMX so it will not submit the form again. Next time you click on the input element HTMX submits the form with the previously selected value.
To fix the issue just remove the hx-trigger attribute. For input fields the default trigger is already the change event, therefore HTMX will submit the form when pickadate.js enters the selected date into the input field.

Related

Bootstrap-Select not updating on mobile

I am using Bootstrap-select for multi-select items with Django. It works fine on desktop, but when the native mobile drop-down is enabled, the selected values of the dropdown do not populate.
HTML
<!-- start product brand info -->
<div class="row">
<div class="col-md-6 col-xs-*">
<div>
<label for="id_brand-name" class="form-label">Products</label>
</div>
<select multiple class="form-control selectpicker mb-3" id="id_brand-name" name="brand-name" mobile="true" multiple required>
{% for product in products %}
<option value="{{product.id}}" id="optiion">{{ product.brand.name | title }} - {{product.name | title}}</option>
{%endfor%}
</select>
<div class="invalid-feedback">
Select at least one product.
</div>
</div>
</div>
<!-- end product info -->
<script>
//Enble native drop down on mobile
window.onload = function () {
$('#id_brand-name').selectpicker({
container: 'body'
});
if( /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent) ) {
$('#id_brand-name').selectpicker('mobile')
}};
</script>
No matter how many items are selected, the selector always shows Nothing selected. When the data is posted, it is apart of the form though.
I like to use bootstrap-select, but this problem has been bothering me for a long time
Similar questions can be found on github, but no perfect answer.
The reason is that it will always refresh his title, no matter how remove() is done externally, and you can't change it's style on iphone Safari and Firefox.
So my solution is: if you can't remove it, then join it.
You have to change the original file: bootstrap-select.js
Search for: bs-title-option, next line you will find:
this.selectpicker.view.titleOption.value = '';
And add two lines down:
this.selectpicker.view.titleOption.value = '';
// new add
this.selectpicker.view.titleOption.disabled='true';
this.selectpicker.view.titleOption.textContent=this.options.title;
//
Done
When you use $('#id_brand-name').selectpicker('mobile')
The title text show on first empty option, and it can't be choose.
try it!

Call a modal (Bootstrap 5) from Jinja2 templating

I'm likely the nth person to have asked this question but am having trouble parsing the relevant documentation; I want to add a modal dialogue to a button declared with a function in Jinja2.
The code for the form where I want the modal to be called is below - the modal should be called when the shutdown button (last line) is pressed, to confirm this action.
<form class="form form-horizontal" method="post" role="form"
style="text-align: center;">
{{ power_form.hidden_tag() }}
{% if power_status == "None" %}
{{ power_form.power_on(class_="btn btn-default") }}
{{ power_form.power_cycle(class_="btn btn-default") }}
{{ power_form.power_off(class_="btn btn-default") }}
The modal dialogue is called 'Shutdown Confirmation' and has been declared later. I am unsure how to call it as online examples show the button declaration is used instead of the {{. Here is the bootstrap documentation for this:
<button type="button" class="btn btn-primary" data-bs-
toggle="modal" data-bs-target="#exampleModal">
Launch demo modal
</button>
With Bootstrap 5 you can activate the modal panel by data attributes or JS API. I assume you want to use the data-attribute method.
With WTForms you can pass any attribute to the rendered element, just replace any dash with underscore (because Python syntax), and WTForms will change them back to dashes:
{{ power_form.power_off(type="button", class_="btn btn-default", data_bs_toggle="modal" data_bs_target="#exampleModal") }}
Change the #exampleModal to the corresponding selector of your modal element. We also change the type of the button from the default submit to simple button, so it wont submit the form, just open the modal window.

Can we load Livewire Component on clicking some button?

I have a Livewire component with a button, and I want to append a form (i.e another livewire component) by clicking that button. Like we do that by using ajax, requesting to the backend to load HTML and append with jQuery. So question is, can we do the same with Livewire?
<div>
<button wire:click="loadFormComponent">Add a Link</button>
<div>
here I want to append the result of `loadFormComponent`
</div>
</div>
bellow is Form Component
<div>
<form wire:submit.prevent="addLink">
<div>
<input type="URL" wire:model="URL" placeholder="https://example.com">
<button type="submit">Save Link</button>
</div>
</form>
</div>
Yes, you can display parts of your blade -including other Livewire components- under conditions. You don't need AJAX for this. You make the section of your Blade file conditional:
<div>
<button wire:click="$set('showFormComponent', true)">Add a Link</button>
#if ($showFormComponent)
<div>
here I want to append the result of `loadFormComponent`
</div>
#endif
</div>
and in your livewire component you need to add:
public $showFormComponent;
On click, the variable $showFormComponent will be set to true and view will be re-rendered including the component.

Display Form Field based on other Fields within the Django Form

I am trying to create a way to only display certain fields within a django form based on the bound data of another field within that same form. I'm familiar with the idea of form.field.bound_type but I'm not sure how to continually check for state change on a field in a form and update the other field accordingly. Something like if you were filling out an application and it asked if you've committed a crime, if you click yes then a details text area pops up.
I'm using:
Django 1.8.4
Bootstrap3 6.6.2
As it pertains to this question. Here is what I've currently got with the field values edited for work protection. It does SORT of work. Meaning the form is fine, the if statement works initially but it doesn't reevaluate the if once the specified field has changed.
<form action= "/send_email/" method="post" class='col-sm-5'>
{% csrf_token %}
{% bootstrap_field form.field_one%}
{% bootstrap_field form.field_two%}
{% bootstrap_field form.field_three%}
{% if form.field_three.bound_data == "A Value" %}
{% bootstrap_field form.field_four%}
{% endif %}
{% buttons %}
<button type="submit" class="btn btn-primary">
{% bootstrap_icon "glyphicon glyphicon-ok-circle" %} Submit
</button>
{% endbuttons %}
</form>
Solution:
With Birdie's help I was able to figure out the solution. For anyone who has hit this same Django related problem here is how you add or remove fields based on another field in the same form.
<script>
// function that hides/shows field_four based upon field_three value
function check_field_value(new_val) {
if(new_val != 'A value') {
// #id_field_four should be actually the id of the HTML element
// that surrounds everything you want to hide. Since you did
// not post your HTML I can't finish this part for you.
$('#field_four').removeClass('hidden');
} else {
$('#field_four').addClass('hidden');
}
}
// this is executed once when the page loads
$(document).ready(function() {
// set things up so my function will be called when field_three changes
$('#field_three').change( function() {
check_field_value(this.value);
});
});
</script>
<form action= "/send_email/" method="post" class='col-sm-5'>
{% csrf_token %}
{% bootstrap_field form.field_one%}
{% bootstrap_field form.field_two%}
<div id="field_three">{% bootstrap_field form.field_three%}</div>
<div id="field_four">{% bootstrap_field form.additional_notes %}</div>
{% buttons %}
<button type="submit" class="btn btn-primary">
{% bootstrap_icon "glyphicon glyphicon-ok-circle" %} Submit
</button>
{% endbuttons %}
</form>
You cannot do this within the template because the template is executed on the server side, but the user interaction occurs on the client side.. in the browser. This must be done in javascript and run in the browser.
Here is an example of jQuery code which does this. I did not test it so it may need tweaking, but this should get you in the right direction.
You will need to look at your HTML to determine the id of the element you actually want to hide() and show(). Normally you would have some kind of HTML element (eg. a DIV) surrounding both the field or fields you want to hide as well as the label(s).. and you would hide everything at once by hiding the element which contains all the fields, rather than each individual field itself.
If you add the HTML surrounding field_four to your question, I will update the answer to work with what you've got...
<script>
// Ideally this script (javascript code) would be in the HEAD of your page
// but if you put it at the bottom of the body (bottom of your template) that should be ok too.
// Also you need jQuery loaded but since you are using bootstrap that should
// be taken care of. If not, you will have to deal with that.
// function that hides/shows field_four based upon field_three value
function check_field_value() {
if($(this).val() == 'A Value') {
// #id_field_four should be actually the id of the HTML element
// that surrounds everything you want to hide. Since you did
// not post your HTML I can't finish this part for you.
$('#id_field_four').hide();
} else {
$('#id_field_four').show();
}
}
// this is executed once when the page loads
$(document).ready(function() {
// set things up so my function will be called when field_three changes
$('#id_field_three').change(check_field_value);
// set the state based on the initial values
check_field_value.call($('#id_field_three').get(0));
});
</script>
Thanks all for the contribution, but I could not get the code above work. This is my solution:
<script>
function hideField() {
check = document.getElementsByName("put your field name here")[0].value;
response = document.getElementById("put your id here");
if (check === "Open") {
response.style.display = "none";
}else{
response.style.display = "block";
}
}
</script>
The key is to figure out Id for the field you want to hide, and name for the field you check. I did NOT use DIV container to assign the id and the name, but inspected rendered HTML page with developer tools in the browser. Let me know if you have questions or want more detailed explanation.
I found this question very interesting. I have updated it with bootstrap 4.0v, with the following script, I hope it can help someone:
<script>
// function that hides/shows field_four based upon field_three value
function check_field_value(new_val) {
if(new_val != 'A value') {
// #id_field_four should be actually the id of the HTML element
// that surrounds everything you want to hide. Since you did
// not post your HTML I can't finish this part for you.
$('#field_four').removeClass('d-none');
} else {
$('#field_four').addClass('d-none');
}
}
// this is executed once when the page loads
$(document).ready(function() {
// set things up so my function will be called when field_three changes
$('#field_three').change( function() {
check_field_value(this.value);
});
});
</script>
<form action= "/send_email/" method="post" class='col-sm-5'>
{% csrf_token %}
{% bootstrap_field form.field_one%}
{% bootstrap_field form.field_two%}
<div id="field_three">{% bootstrap_field form.field_three%}</div>
<div id="field_four">{% bootstrap_field form.additional_notes %}</div>
{% buttons %}
<button type="submit" class="btn btn-primary">
{% bootstrap_icon "glyphicon glyphicon-ok-circle" %} Submit
</button>
{% endbuttons %}
</form>

Django form submit on dropdown selection rather than submit button

I have a table with lots of job data, and I have a dropdown menu and a submit button, which acts as a filter, so that the table only displays jobs based on the filter:
<form>
<select id="user_id_dropdown" name="user_id">
<option disabled selected>Filter by Username</option>
<option value="all">All Usernames</option>
<option disabled>────────────</option>
{% for user in users %}
<option value={{ user.id }}>{{ user.username }}</option>
{% endfor %}
</select>
<input id="filter" class="btn btn-primary" type="submit" value="Filter" />
</form>
<table>
...
How I've done it with the button is such that the user_id of the username is passed as a query string and my view handles it. Upon selecting a username (say it's user_id is 4) and clicking the submit button, the url is:
http://...jobs?user_id=4
Then I have a table below where all the jobs displayed are now only those created by user_id 4.
The thing is, now I just want to do away with the submit button and just submit the form on dropdown selection.
I've tried to give the form a name and to submit when there is a change on selection:
<form name='filter' method=POST>
<select id="user_id_dropdown" name="user_id" onChange="filter.submit();">
...
But this doesn't seem to work. It does seem like the page reloads (similar to the submit button), but the table data doesn't change. What am I missing out here?
I tried this:
onChange="form.submit();"
and it worked. Seems the name is not necessary.
Try putting this on your onchange attr:
document.filter.submit();
If this fails, give your form an ID attribute and do:
document.getElementById('youFormId').submit();
You could also send it as a GET paramenter like:
onchange="window.locatio.href+='?v='+this.value;"
By the way this question has little relation with Django, you should tag it html/javascript next time you ask about this kind of stuff.