I have forms that are rendered dynamically based on values in a dictionary. They are given an ID based on the key value. They are all initially hidden.
<div id="subforms" style="display: none" >
{%for k, v in options.items() %}
<h3>{{k}}:</h3>
<form id= "{{k}}">
{% for option in v %}
<label>{{option}}</label>
<input type="checkbox" name="{{option}}_enabled">
{% endfor %}
</form>
{% endfor %}
</div>
I now create an input list with those same keys:
<form action="/action_page.php">
<input list="tables" id="tablelist" >
<datalist id="tables">
{% for key in options.keys() %}
<option value={{key}}>
{% endfor %}
</datalist>
</form>
Last I have Javascript used to listen to the tablelist element and select a form based on the inputlist's value.
const tables = document.getElementById("tablelist")
const subform_block = document.getElementById("subform_display")
const forms = document.getElementById("subforms")
tables.oninput = () => {
let form =
forms.querySelector('form[id="${tables.value}"]');
if(form){
subform_block.innerHTML = form.outerHTML;
}
else {
subform_block.innerHTML = "not found";
}
}
The querySelector is not working. I confirmed that the HTML is rendered correctly and the IDs are consistent, but my querySelector is unable to find any of the forms. What is wrong?
Changed:
let form = forms.querySelector('form[id="${tables.value}"]');
to:
document.querySelector('form[id=' + tables.value + ']');
And it works. Not sure why ${tables.value} wasn't recognized properly
Related
I have a page that adds textboxes dynamically, and on the dynamically added text boxes, I want to access the relevant context variable.
To do so, I somehow need to use the 'i' variable inside the javascript code/template code to reference it's value in the context.
Here's my javascript code where I need to use 'i' as an index for a context list:
<script>
var i = 2
function add_field()
{
var table = document.getElementById("tbl_location").getElementsByTagName('tbody')[0];
var input_name = document.createElement("th")
input_name.appendChild(document.createTextNode("S/N " + i))
var input_tb = document.createElement("td")
var input = document.createElement('input');
input.setAttribute("name", "sn" + i);
{% if curr_sn.i %}
input.setAttribute("value", {{ curr_sn.i }});
{% endif %}
input_tb.appendChild(input)
var input_row = document.createElement("tr")
input_row.appendChild(input_name)
input_row.appendChild(input_tb)
table.appendChild(input_row);
i++;
}
The specific lines are:
{% if curr_sn.i %}
input.setAttribute("value", {{ curr_sn.i }});
{% endif %}
Where 'i' is a javascript veriable, and curr_sn is a list the python code gives as a context.
The html part where I need to use 'i':
{% for i in max_board_list %}
{% if curr_sn.i %}
<tr>
<th>S/N {{ i }}</th>
<td><input type="text" name="sn{{ i }}" value="{{ curr_sn.i }}"></td>
</tr>
{% endif %}
{% endfor %}
Here 'i' is generated by a for loop, but I still need to access the same list.
How can I do this in both parts of code? All I could find the the opposite way which already works for me (using django template in javascript code).
Thanks
EDIT: for javascript side i found a solution (since I noticed that anyway my code is redundant and tries to do similar things in js&html). For the javascript part I use:
{% if curr_sn|length > 1 %}
i = {{ curr_sn|length }}
{% endif %}
Just to keep counting from the current number of boards.
Now I need to solve the html parts to display the saved boards in the context variable curr_sn
Found a solution for the html part as well.
I just use custom filters to do the checking.
I've registered two filters:
#register.filter(name='is_in')
def is_in(index, list):
if list is None:
return False
try:
list[int(index)]
return True
except:
return False
#register.filter(name='get_value')
def get_value(index, list):
if list is None:
return False
try:
return list[int(index)]
except:
return None
And in the HTML part I use the following:
{% for i in max_board_list %}
{% if i|is_in:curr_sn %}
<tr>
<th>S/N {{ i }}</th>
<td><input type="text" name="sn{{ i }}" value="{{ i|get_value:curr_sn }}"></td>
</tr>
{% endif %}
{% endfor %}
Hope this will help for others with the same problem.
I want to populate a select field with onclick but i don't know how to filter the array to only the value of the clicked button. With my code all the array objects populate the input and I want only the value of the clicked button to populate the input. Any hints?
<input type="hidden" id="id_user" value="{{user.id}}">
<select id="id_entities" class="js-example-programmatic" style="width:100%" multiple="multiple">
{% for teu in tagEntityUsers %}
<option value="{{teu.entity_id}}" class="js-programmatic-set-val">{{teu.entity}}</option>
{% endfor %}
</select>
{% for teu in tagEntityUsers %}
<tr>
<td>{{teu.entity}}</td>
<td>{{teu.user}}</td>
<td><button id ="{{teu.entity_id}}" class="js-programmatic-set-val" value="{{teu.entity_id}}" onclick="populate_box(this.id)">add entity</button></td>
</tr>
{% endfor %}
<script>
var $example = $(".js-example-programmatic");
var arr = $.map($('.js-programmatic-set-val'), function (el) { return el.value; });
function populate_box() {
$example.val(this.arr).trigger("change"); }
</script>
I'll add a prefix to button id and set jQuery listener to it:
$("button[id^='prefix_']").click(function(){
value = $(this).attr('data-value');
$('#id_entities option[value="' + value + '"]').prop("selected", true).change();
});
So, the button will be:
<button id="prefix_{{teu.entity_id}}" class="js-programmatic-set-val" data-value="{{teu.entity_id}}">add entity</button>
UPD: Code above is fixed, see it in action. And I highly recommend to change value attribute on button to data-value, read more about data-* attributes
Here I found the following helpful javascript to dynamically add a form to my python/django template. Here is the code:
function updateElementIndex(el, prefix, ndx) {
var id_regex = new RegExp('(' + prefix + '-\\d+)');
var replacement = prefix + '-' + ndx;
if ($(el).attr("for")) $(el).attr("for", $(el).attr("for").replace(id_regex, replacement));
if (el.id) el.id = el.id.replace(id_regex, replacement);
if (el.name) el.name = el.name.replace(id_regex, replacement);
}
function addForm(btn, prefix) {
// Make a variable and assign to it a string convertd to an integer. The string is the variable
// with the the #id assigned to it. The value of this attribute is saved. The value will equal
// the current number of forms
var formCount = parseInt($('#id_' + prefix + '-TOTAL_FORMS').val());
// Find the element with the given class, and clone it after loading the 0 index get request.
// Var row is now a cloned row.
var row = $('.dynamic-form:first').clone(true).get(0);
$(row).removeAttr('id').insertAfter($('.dynamic-form:last')).children('.hidden').removeClass('hidden');
$(row).children().not(':last').children().each(function() {
updateElementIndex(this, prefix, formCount);
$(this).val('');
});
$(row).find('.delete-row').click(function() {
deleteForm(this, prefix);
});
$('#id_' + prefix + '-TOTAL_FORMS').val(formCount + 1);
return false;
}
function deleteForm(btn, prefix) {
$(btn).parents('.dynamic-form').remove();
var forms = $('.dynamic-form');
$('#id_' + prefix + '-TOTAL_FORMS').val(forms.length);
for (var i=0, formCount=forms.length; i<formCount; i++) {
$(forms.get(i)).children().not(':last').children().each(function() {
updateElementIndex(this, prefix, i);
});
}
return false;
}
Then I have my following template.
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% crispy form form.helper %}
{% load staticfiles %}
{% block extrahead %}
{% endblock %}
{% block blurb %}
<h1>Upload Samples</h1>
<p>Upload A Single Sample, Or A Batch Of Samples</p>
{% endblock %}
{% block form %}
<style>
tr th {text-align:center;}
</style>
<!-- Display formset -->
<form id="myForm" method="post" action="">
{{ formset.management_form }}
<div class='table'>
<table class='no_error' id='id_forms_table' border="2px" cellpadding="0" cellspacing="0">
<thead>
<tr>
<th></th>
</tr>
</thead>
<tbody>
{% for form in formset %}
<tr id="{{ form.prefix }}-row" class="dynamic-form">
<td></td>
<
<td {% if forloop.first %} class="hidden"{% endif %}>
<a id="remove-{{ form.prefix }}-row" href="javascript:void(0)" class="delete-row"><input type='button' value='Remove Row' class='delete-row'></a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</form>
<!-- Dynamic Formset javascript -->
<script type="text/javascript">
$(function () {
$('.add-row').click(function() {
return addForm(this, 'form');
});
$('.delete-row').click(function() {
return deleteForm(this, 'form');
})
$('.add-10-rows').click(function() {
return addForm(this, 'form');
})
})
</script>
<input type='button' value='Add Row' class='add-row'>
<input type='button' value='Add 10 Rows' class='add-10-rows'>
<div style='padding-top:20px'>
<input class='btn btn-primary' type='submit' value='Upload' />
</div>
{% endblock %}
This works great, but now I would like to adjust this to allow for the addition of n rows. I will make a form that takes in an interger, then when a button is pressed, I would like to add that number of forms instead of one at a time. I am quite new to javascript/jquery, but I figured the psudo code would look something like this:
function addNForms(btn, prefix, n){
for(var i = 0; i < n; i++){
// Perform the same logic as addForm.
}
}
Unfortunately I have not been able to figure it out. I have performed the function logic n times in a for loop, but the result was that only one form at a time was being made. Which part of the addForm function can I put in a for loop to achive this goal?
I would like to use a variable which I passed to the html template via flask in a javascript code. Here is an example: I have a list which I passed to the html template and I run a for loop over that list
<body>
{% for group in groups %}
<input class="group_sel" type="checkbox" id="{{ group[0] }}" name="groupstoselect[]" value="{{ group[0] }}">{{ group[0] }}
</input><br>
{% endfor %}
</body>
here I have a list (groups) and loop over that list. My question is if I want to do that loop in a jquery statement, how would that work?
thanks
carl
Server side template generates list of checkboxes with the same class .group_sel. Now on the client you can select this collection and loop over it with this code:
$('.group_sel').each(function() {
console.log( this.value );
});
Also make sure your HTML is valid, remove </input>, it should be:
{% for group in groups %}
<input class="group_sel" type="checkbox" id="{{ group[0] }}" name="groupstoselect[]" value="{{ group[0] }}"> {{ group[0] }}
<br>
{% endfor %}
Theres no big change if you just want to move the Jinja to a javascript that works in the same way as defining it in the html.
<script type="text/javascript">
var d = {{ groups }}
d.forEach(function(entry) {
console.log(entry);
});
You might have to add the |safe filter to groups. {{ groups|safe }}
The safe filter explicitly marks a string as "safe", i.e., it should not be automatically-escaped if auto-escaping is enabled. See http://jinja.pocoo.org/docs/dev/templates/#working-with-automatic-escaping
I have a dynamically populated html table that can be edited by an user (they can add rows delete them, etc -this is managed through javascript-).
The problem is, even when the table is inside the form, the server doesn't get any post data from it.
Here is the code for the template:
{% extends 'base.html' %}
{% block content %}
<script>
function add_row(){
var table = document.getElementById("body");
var row = table.insertRow(table.rows + 1);
var cell = row.insertCell(0);
var combo1 = document.createElement("select");
var option;
{% for opt in options_all %}
option = document.createElement("option");
option.setAttribute("value", "{{ opt.id }}");
option.text="{{ opt.description }}";
combo1.add(opcion);
{% endfor %}
cell.appendChild(combo1);
}
</script>
<form action='.' method='post'>
{% csrf_token %}
{{ form.as_p }}
<input type="button" value="New row" onclick="add_row()">
<table id="table_id">
<thead>
<tr>
<th>
Options
</th>
</tr>
</thead>
<tbody id="body">
</tbody>
</table>
<input type='submit' value='Submit'>
</form>
{% endblock %}
How can I get the data from the select elements?? Thanks!!
Edit: I have checked the request element and request.post doesn't have the desired data
SOLVED.
You have to specify a name for each select element inside the table. (Not the option elements)