Django Form - Conditional field display using JQuery - javascript

This implementation is using Django 4.0 and Bootstrap5 with CrispyForms.
Objective: The user selecting a value in one field determines whether another field is shown or hidden. By default, the conditional field has a value of "No". The impacted field will be shown when the user changes the conditional field to the value of "Yes". Specifically, if the user selects "Yes" for the field Recurring event the field Recurrence pattern will be shown.
Please note that the implementation of Class Based Views in the template is working great. Here's a screenshot of the form (with the conditional field requirement not working):
Existing code (relevant snippets only)
Here's what I have in models.py:
YES_NO_CHOICES = [
('No', 'No'),
('Yes', 'Yes'),
]
RECURRENCE_PATTERN_CHOICES = [
('---', '---'),
('Daily', 'Daily'),
('Weekly', 'Weekly'),
('Monthly', 'Monthly'),
('Yearly', 'Yearly'),
]
class Event(models.Model):
event_name = models.CharField(
max_length=70,
help_text='''Enter a name for the event. This is a required field and is limited to 70 characters.'''
)
recurring_event = models.CharField(
max_length=5,
choices=YES_NO_CHOICES,
default='No',
help_text='''Is this a one off event or will it recur? Selecting Yes will open up additional fields.'''
)
recurrence_pattern = models.CharField(
max_length=10,
choices=RECURRENCE_PATTERN_CHOICES,
default='---',
help_text='''Select the recurrence pattern for this event.'''
)
Here is what I have in forms.py:
class EventStaffSummaryUpdateView(UpdateView):
"""Event Staff Update view - allows staff to change event details"""
model = Event
template_name = 'events/event_staff_summary_form.html'
fields = [
'event_name',
'recurring_event',
'recurrence_pattern',
]
context_object_name = 'event'
In the event_staff_summary.html template (using inheritance) I have:
<div class="content-section">
<form method="POST">
{% csrf_token %}
<fieldset class="form-group">
<div id="event_name">
{{ form.event_name|as_crispy_field }}
</div>
<div id="recurring_event">
{{ form.recurring_event|as_crispy_field }}
</div>
<div id="recurrence_pattern">
{{ form.recurrence_pattern|as_crispy_field }}
</div>
</fieldset>
<div class="form-group pt-4 pb-2">
{% if page_type == 'Update' %}
<button class="btn btn-BlueGray" type="submit">Update Event</button>
{% else %}
<button class="btn btn-BlueGray" type="submit">Create Event</button>
{% endif %}
</div>
</form>
</div>
In the base.html template I am using the Google JQuery link (found on my journey as a simple way of implementing JQuery):
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
As you can see with the screenshot, the form itself is displaying perfectly. So models, form and even the html is working to this point. The update class based view is working with the DB. All that remains is to show or hide Recurrence pattern based on the value in the field Recurrence event. Which brings me to...
What have I tried so far?
Of the 10+ JS snippets I've come across and tried, here is the leading candidate that seems to be closest. I really want this to work as it makes sense to me.
<script>
$("#recurring_event").change(function() {
if ($(this).val() === "Yes") {
$('#recurrence_pattern').show();
} else {
$('#recurrence_pattern').hide();
}
});
</script>
Request
Has anyone got suggestions on how I can get this working? I've got requirements for a number of conditionally displayed fields. I'd prefer to not work from checkboxes as typically this client prefers to explicitly have the user click Yes or No as this is clearer to them (they feel there is no chance of ambiguity with a closed question and a yes/no choice).
This requirement of conditional fields in forms is implemented in many instances, so I know I'm not seeking the impossible. I am hoping that there's a simple refinement (or clean up) of the javascript snippet I've posted. I also welcome other JS suggested snippets too.
Thanks in advance for your help.
ANSWER (Feb 27)
Following the issuance of a bounty #Swati came through with a solution posted in a fiddle here: https://jsfiddle.net/L8ke03hc/ As soon as they post the answer I'll award the Bounty. I'd like to thank Tony for coming really close and getting me on the right track. The final piece of the puzzle was the .trigger event to call the change function.
<script>
$(document).ready(function() {
$("#id_recurring_event").change(function() {
if ($(this).val() === "No") {
$('#div_id_recurrence_pattern').hide();
} else {
$('#div_id_recurrence_pattern').show();
}
});
$("#id_recurring_event").trigger("change")
})
</script>
Thanks to all that came through on this one. This was a profound learning experience for me. I really need to spend some time learning JS. That said, I've got the solution. May anyone trying to build conditional logic in Django/CrispyForms benefit from this. I am so grateful for the Open Source community! <3

Update library to
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
Wrap function in a DOM ready event.
Update selector to a <select> (from div). Do this by inspecting DOM.
End result:
(function($){
$(document).ready(function(){
$("#id_recurring_event").change(function() {
if ($(this).val() === "Yes") {
$('#id_recurrence_pattern').show();
} else {
$('#id_recurrence_pattern').hide();
}
});
})
})(jQuery);

You can use trigger() method to achieve this . So, whenever your page gets load called this to execute the change event which is attached on select-box .
Working code :
$(document).ready(function() {
$("#id_recurring_event").change(function() {
if ($(this).val() === "No") {
$('#recurrence_pattern').hide();
} else {
$('#recurrence_pattern').show();
}
});
$("#id_recurring_event").trigger("change")
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<div class="content-section">
<form method="POST">
<fieldset class="form-group">
<div id="event_name">
ABC ..
</div>
<div id="recurring_event">
<select id="id_recurring_event">
<option value="Yes">Yes</option>
<option value="No" selected="true">No</option>
</select>
</div>
<div id="recurrence_pattern">
somethings ..................
</div>
</fieldset>
</form>
</div>

Related

How to add HTML templates in a page?

I made an template of a price box, and I want that every time that I click a button a new price box shows up in the page.
Just a simple example for my template:
<div>
<h1> Product: {productName} </h1>
</div>
And everytime that I click a button, I pass the name and this template will show in the page.
I was looking and I saw the template tag with javascript and another solutions like Mustache. Which approach would be better and more readable?
Not exactly sure what your asking but this would be very simple using pure javascript. Please see the following code snippet:
function addProduct() {
const productName = document.querySelector('#productName').value;
productName ? document.querySelector('#products').innerHTML += `
<div><h1>Product: ${productName}</h1></div>
` : alert('Enter Product Name');
}
document.querySelector('#add').addEventListener('click', () => addProduct());
<input type="text" id="productName">
<button id="add">Add Product</button>
<div id="products"></div>

Angularjs, checking if radio buttons in form have been selected

I'm starting with AngularJS, and I'm building a multi-step form where user has to fill different pages. When finished a page, he's allowed to press a next button and fill the following page.
For the first page, I've built in the HMTL a form (named pageOneForm), with different text input fields, marked as required, and in the relative controller I'm doing this watch:
$scope.$watch('pageOneForm.$valid', function(validity) {
ModelData.actualPageCompleted = validity;
})
And it works like a charme. My model (ModelData) is updated.
I was trying to apply the same logic to the following part of the app, the second page. Instead of input text, the user has to select two options from 2 different radio buttons groups.
So I built in the html a list of buttons via ng-repeat :
<div ng-Controller="PageTwo" ng-show='data.actualPage == 2'>
<form name="pageTwoForm">
<h3>General Information > Knowledge About </h3>
<div>
<b>User</b>
<div ng-repeat="option in userOptions">
<input type="radio" name="userGroups" ng-model="data.knowledgeAboutUser" ng-value="option.id" id="{{option.id}}" required>{{option.text}}
</div>
<div ng-repeat="option in targetGroupUserOptions">
<input type="radio" name = "targetUserGroup" ng-model="data.knowledgeAboutTargetGroup" ng-value="option.id" id="{{option.id}}" required>{{option.text}}
</div>
</div>
</form>
and I've implemented the same code as above in its controller:
$scope.$watch('pageTwoForm.$valid', function(validity) {
ModelData.actualPageCompleted = validity;
})
but apparently it doesn't work, and in my model actualPageCompleted is always true...
What am I doing wrong?
Thanks
I did my best to create a controller with some dummy data to get a fiddle working with your example code. Here is the fiddle You need to force the $digest cycle to update your form's validity state on ng-click for the radio buttons (see this SO post for more details), which is why the method
$scope.forceDigest = function(){
setTimeout(function(){ $rootScope.$$phase || $rootScope.$apply(); });
};
is necessary. Alternatively, you can get rid of the method call and uncomment the html code
<h3 ng-show="false">{{data.knowledgeAboutTargetGroup}}</h3>
<h3 ng-show="false">{{data.knowledgeAboutUser}}</h3>
in the fiddle to force the form object to update as well.
And I would make sure that ModelData.actualPageCompleted is not retaining its true value from when pageOneForm.$valid became true and it was set.
I hope that this helps!

Symfony UniqueEntity Constraint and javascript

i'm new to symfony, and don't know much about javascript!
i created a symfony form, and added a UniqueEntity constraint on the name and firstname, so it's not possible to add a same person twice in the database.
#UniqueEntity(fields={"firstname","name"}, message="this person already exists")
it works pretty well!
but in this case, i would like symfony to show me a javascript window with a message and 2 choices. for example: "joe smiley already exists! would you like to add an homonym? yes / no
does anyone know how to do this?
Much thanks
No, this validation is strictly server-side.
You should try some JS validation libraries like: http://rickharrison.github.io/validate.js/ (just an example)
well you could try to find if is there and error message for that field, and that will depend on how are you displaying your form fields, i strongly strongly recommend you to customize your form rendering to suit your needs, here is and example of what can you do.
<div class="control-group {% if(form_errors(form.descripcion)|length) %} error {% endif %} ">
{{ form_label(form.descripcion, null, { 'label_attr': {'class': 'control-label'} }) }}
<div class="controls">
{{ form_widget(form.descripcion) }}
{{ form_errors(form.descripcion) }}
</div>
</div>
that will render this when the validation shows and error message
<div class="control-group error ">
<label class="control-label required" for="AreaComercio_descripcion">Descripcion</label>
<div class="controls">
<input type="text" id="AreaComercio_descripcion" name="AreaComercio[descripcion]" required="required" >
<span class="help-inline">This value should not be blank.</span>
</div>
</div>
so you have to ask if is there an span sibling with the class help-inline if you dont know how to custimze your form rendering take a look at this and another advice use jquery, there are lots of people using it, jquery have an strong comunity that will help you if you are in the need, that said, i will use jquery to ilustrate what you can do to solve your problem.
if($("#AreaComercio_descripcion ~ .help-inline").length) { // here using sibling selector filter to ask if there a siblig with the class help-inline
var invalid_value = $("#AreaComercio_descripcion").val(); // asking for the field value thru its id attribute
confirm(invalid_value+" already exists! would you like to add an homonym ?");
}
using javascript confirm is the easiest way to do what you want but you can use other more flexible options like jquery dialog or you could try to install twitter bootstrap in your symfony installation, well i guess thats all i hope it will be usefull to you

dynamic window.find not working with jQuery

I can't for the life of me figure out why this isn't working.
I want to search the current page for text using a search box. I googled and found this: http://www.javascripter.net/faq/searchin.htm . I implemented the code into my site, but it doesn't work. the function ( findString() ) works, but only when I hard-code a string (as in i can't use javascript or jquery to get the value of a text input). I made this fiddle: http://jsfiddle.net/alyda/CPJrh/4/ to illustrate the problem.
You can uncomment different lines to see what I've tested.
jQuery has a method :contains() that will make easier what you are looking for.
Take a look here: fiddle
$("button[type='submit']").click(function () {
var string = $('#search').val();
var matched = $('li:contains(' + string + ')');
matched.css('color','red');
console.log(matched);
return false;
});
I found a fix (sort of). It seems that the input needs to be placed well AFTER the content to be searched in the DOM. That means I've done the following:
<section class="content">
<h2>Fire</h2>
<h3>Fire Extinguishers</h3>
<ul>
<li>Model 240</li>
<li>Model C352, C352TS</li>
<li>Model C354, C354TS</li>
</ul>
...
<div id="navbar">
<ul>
...
</ul>
<input id="search" type="text" class="form-control pull-left" placeholder="Search for part number">
<button id="submit" type="submit" class="btn btn-default pull-left" style=" margin-top:6px;">Search</button>
</div>
as you can see, I've moved the input (which is in the navbar div) BELOW all of the text I want to search, and used CSS to programmatically place the navbar at the top of the page. I don't particularly like this setup (as it messes with the flow of content) but since I was looking for the quickest and simplest implementation of a single-page search, it will have to do.
I would still love to know why this happens, when the javascript is at the end of the DOM where it belongs...
In firefox I noticed that the fiddle (v4) as given in the question worked, but not in the way the asker expected it to.
What happens in firefox is that the function does find the value..: you have just entered it in the input-field. Then the browser's find method seems to hang in the 'context' of the input 'control' and doesn't break out of it. Since the browser will continue to search from the last active position, if you select anything after the input-field, the function works as expected. So the trick is not to get 'trapped' in the input-field at the start of your search.
A basic (dirty) example on how to break out of it (not necessarily the proper solution nor pure jquery, but might inspire a useful routine, since you now know the root of the problem in FF):
$( "button[type='submit']" ).click(function(){
var tst=$('#search').val(); //close over value
$('#search').val(''); //clear input
if(tst){ //sanity check
this.nextSibling.onclick=function(){findString( tst );}; //example how to proceed
findString( tst ); //find first value
} else { alert('please enter something to search for'); }
return false;
});
Example fiddle is tested (working) in FF.
PS: given your specific example using <li>, I do feel Sergio's answer would be a more appropriate solution, especially since that would never run line: alert ("Opera browsers not supported, sorry..."), but the proper answer to your window.find question is still an interesting one!
PS2: if you essentially are using (or replicating) the browser's search-function, why not educate the user and instruct them to hit Ctrl+F?
Hope this helps!
I had same problem in an angularjs app and I fix it by changing DOM structure.
my HTML code was something like this:
<body>
<div class="content" >
<input class="searchInput" />
<p>
content ....
</p>
</div>
</body>
and I changed it to something like this:
<body>
<div class="search">
<input class="searchInput" />
</div>
<div class="content">
<p>
content ....
</p>
</div>
</body>
Note: I'm aware that this topic is old.

Mashup: Select Box / Disabled Input / Getting Info From Database

I am creating a form, where people can either choose a new team and then input its location or if they select a team from the list, I would like the input to be disabled - and the selected team's location to be given in the input box. This is my code so far. It doesn't work. :(
<select id="chooseTeam" name="chooseTeam" data-placeholder="Select Team">
<option></option>
{% for team in teams %}
<option>{{team.name}}</option>
{% endfor %}
<option>New Team</option>
</select>
<input type="text" id="input_location" name="input_location"/>
In a separate JS File:
$(document).ready(function() {
$('#chooseTeam').on('blur', function() {
if (form.chooseTeam.value != "New Team" && form.chooseTeam.value != "Select Team") {
$("#input_location").html("{{team.location}}");
document.getElementById("input_location").disabled = true;
}
});
});
MINI-UPDATE
Apparently, AJAX is needed for this. I am brand new to Javascript (3 days old) so I don't know how AJAX works yet but I am at this moment googling it to try and figure it out.
Oh, and Merry Christmas, everybody!
"#code-on-christmas"
It seems that you are using Django and jQuery. Is that true? I cannot give you a fully detailed answer at the moment, but the code in the following line cannot work because you are mixing client-side and server-side code.
$("#input_location").html("{{team.location}}");
The expression {{team.location}} cannot be evaluated by Django because it only exists on the client-side in your browser. If you want Django to evaluate what has been chosen on the client-side, you should use AJAX calls between server and client by using $.ajax().
Also, why don't you use jQuery selectors for all of your code? Do something like this:
$(document).ready(function() {
$('#chooseTeam').on('blur', function() {
var currentTeam = $(this).val();
if (!$.inArray(currentTeam, ['New Team', 'Select Team'])) {
$("#input_location").attr('disabled', 'disabled');
}
});
});

Categories

Resources