I work with Symfony and Twig. I currently have a twig page containing a list of products, I display them with a foreach loop and I put pagination to limit the display of products.
I have a form in this page with a checkbox as input and I need to keep my checkbox checked save in session when I go to the next page
I tried to do it with adding this code
there is some code, I added some comment in the pagination view and controller to explain what I tried.
view of my loop :
<form>
<div class="row" >
{% for annonce in annonces %}
<div class="col-12 col-md-6 col-lg-4">
<p class="text text--blue text--bold m-0 text--medium mt-2">
{{ annonce._source.titre }}
</p>
<p class="m-0">{{ 'Réf' }}: {{ annonce._source.reference }}</p>
<div class="d-flex mt-2 text--bold">
<div class="d-flex me-2">
{{ annonce._source.ville }}
</div>
</div>
<div>
<input type="checkbox" name="checkbox[]" id="checkbox_pdf" value="{{annonce._id}}" multiple/>
</div>
</div>
{% endfor %}
</div>
<input type="submit" id="pdf_submit" value="Create PDF" name="submit_pdf" class="btn btn-primary">
</form>
view of the pagination :
// I tried to add a onclick : onclick='document.forms["name"].submit(); return false;' on each pagination link combined with the save of the value in session with my controller but doesn't work
<div class="col d-flex justify-content-between align-items-center">
<div class="d-flex">
{% if page > 0 %}
<a href="#" data-action="pagination" data-uri="{{ path('ajax_annonce_pagination',{'page':0, 'type':'frontoffice'}) }}" data-target="pagination-target">
«
</a>
<a href="#" data-action="pagination" data-uri="{{ path('ajax_annonce_pagination',{'page':page-1, 'type':'frontoffice'}) }}" data-target="pagination-target">
{{ 'Précédent' }}
</a>
{% else %}
<a href="#" disabled="disabled" >
{{ 'Précédent' }}
</a>
{% endif %}
</div>
<div>
<ul class="list-unstyled pagination m-0">
{% for i in (page+1)..(page+4) %}
{% if i <= numberOfMaxPage %}
{% if i == (page+1) %}
<li>
<a href="#" data-action="pagination" data-uri="{{ path('ajax_annonce_pagination',{'page':(i-1), 'type':'frontoffice'}) }}" data-target="pagination-target">
{{ i }}
</a>
</li>
{% else %}
<li>
<a href="#" data-action="pagination" data-uri="{{ path('ajax_annonce_pagination',{'page':(i-1), 'type':'frontoffice'}) }}" data-target="pagination-target">
{{ i }}
</a>
</li>
{% endif %}
{% endif %}
{% endfor %}
</ul>
</div>
<div class="d-flex">
{% if page < (numberOfMaxPage-1) %}
<a href="#" data-action="pagination" data-uri="{{ path('ajax_annonce_pagination',{'page':page+1, 'type':'frontoffice'}) }}" data-target="pagination-target">
{{ 'Suivant' }}
</a>
<a href="#" data-action="pagination" data-uri="{{ path('ajax_annonce_pagination',{'page':numberOfMaxPage-1, 'type':'frontoffice'}) }}" data-target="pagination-target">
»
</a>
{% endif %}
</div>
</div>
JS of the pagination :
$( document ).ready(function() {
$(document).on('click', 'a.pagination',function(e) {
e.preventDefault();
$.ajax({
url: $(this).data('uri'),
}).done(function(html) {
$('#pagination-target').html(html);
$('html,body').animate({scrollTop: $('#pagination-target').offset().top - 80}, 200);
var $scrollable = document.getElementById('pagination-target');
$scrollable.scrollIntoView();
});
});
});
In my controller I render my view like this :
public function search(Request $request, ?SecteurGeographique $secteurGeographique, AnnonceRepository $annonceRepository): Response
{
$selectId = $request->get('checkbox');
$selected = $annonceRepository->findById($selectId);
// I tried to add this code to save my values
if (isset($selectId))
{
$session = new Session();
$session->set('checkbox',$selectId);
}else{
echo 'false';
$session = new Session();
$session->clear();
}
return $this->render('front/annonce/list.html.twig', array(
'annonces' => $results['hits']['hits'],
'total' => $results['hits']['total']['value'],
'website' => $website,
'page' => $request->query->getInt('page')
));
}
It is better to do a save in session my php or in ajax ?
thanks you in advance
Actually, if I understood correctly your code, you don't really need to use session.
I assume that, when you submit the form, then you will need to post all the checkbox value at once to generate your PDF.
If that is try, you should only store the list of the checkboxes directly via Javascript and be sure to send everything when you submit your form.
In this theory, there could be you HTML main page :
<form>
<div class="row" >
{% for annonce in annonces %}
<div class="col-12 col-md-6 col-lg-4">
<p class="text text--blue text--bold m-0 text--medium mt-2">{{ annonce._source.titre }}</p>
<p class="m-0">{{ 'Réf' }}: {{ annonce._source.reference }}</p>
<div class="d-flex mt-2 text--bold">
<div class="d-flex me-2">
{{ annonce._source.ville }}
</div>
</div>
<p>
<input type="checkbox" name="checkbox[]" class="checkboxPDF" value="{{annonce._id}}"/>
</div>
{% endfor %}
</div>
<div id="savedCheckboxes" class="d-none"></div>
<input type="submit" id="pdf_submit" value="Create PDF" name="submit_pdf" class="btn btn-primary">
</form>
Here, you can see that I added the invisible div #savedCheckboxes. That will allow us to save all the checkboxes when you change your pages. I also corrected a little bit your HTML, but nothing major.
Then you should update your pagination javascript :
$(document).ready(function() {
$(document).on('click', 'a.pagination',function(e) {
e.preventDefault();
// Save all the selected checkboxes by moving them to #savedCheckboxes
$('.checkboxPDF:checked').appendTo($('#savedCheckboxes'))
// Do your pagination like you did
$.ajax({
url: $(this).data('uri'),
}).done(function(html) {
$('#pagination-target').html(html);
// If the user come to a previous page, verify if he did selected a checkbox
$('#pagination-target .checkboxPDF').each(function(i, element) {
// If the checkbox already exists in the #savedCheckboxes, then select this checkBox & remove it from #savedCheckboxes
var savedCheckbox = $('#savedCheckboxes .checkboxPDF[value="'+element.val()+'"]')
if(savedCheckbox.length > 0) {
element.click() // Select this checkbox
savedCheckbox.remove() // Remove it from the hidden selection
}
})
$('html,body').animate({scrollTop: $('#pagination-target').offset().top - 80}, 200);
var $scrollable = document.getElementById('pagination-target');
$scrollable.scrollIntoView();
});
});
});
And the magic is done ... When you will submit your form, you will always receive ALL the list of the selected Checkbox, even those that are not displayed anymore because of your pagination.
Related
As I am implementing AJAX, I would like this whole <div> to show up as a consequence of clicking the button in the script below :
<div class="col-12" id="deleteVote">
<div class="row mb-2" >
<div class="col-12">
{% for vote in proposal.votes %}
{% if app.user == vote.user %}
<a href="{{ path('vote_delete',{'slug' : slug, 'proposal' : proposal.id, 'vote' : vote.id}) }}" class="btn btn-light ml-1 btn-sm deleteVote" data-toggle="deleteConfirmation">
<i class="bi bi-trash"></i> {{ 'vote-delete' | trans }}
</a>
{% endif %}
{% endfor %}
</div>
</div>
</div>
Here is the script, I don't really know why the .show is not working :
<script>
$(document).on('click', '.votedFullAgreement', function (){
$.ajax({
url: '{{ path('vote_add', {'slug' : slug, 'proposal' : proposal.id, 'userVote' : 'votedFullAgreement', 'user' : app.user.id }) }}',
type: 'POST',
dataType: 'html',
success: function (){
$('#deleteVote').show();
},
error: function (resultat, statut, erreur) {
}
});
return false;
});
</script>
Btw the script is probably not optimised at all but it's my first one and it's working and sending data...
Thank you !
EDIT 2 :
I don't understand why this if/else doesn't work. When there's a vote, the if works, the button is there when we come back/reload the page, but when there is no vote yet, the .show(); doesn't activate the else which is in display:none.
Is it because as soon as I vote, the if becomes true? (and .show(); needs something previously hidden to display but can't find any...)
{# Standard loop #}
{% if proposal.votedByUser(app.user) %}
<div class="col-12">
<div class="row mb-2">
<div class="col-12">
{% for vote in proposal.votes %}
{% if app.user == vote.user %}
<a href="{{ path('vote_delete',{'slug' : slug, 'proposal' : proposal.id, 'vote' : vote.id}) }}" class="btn btn-light ml-1 btn-sm" data-toggle="deleteConfirmation">
<i class="bi bi-trash"></i> {{ 'vote-delete' | trans }}
</a>
{% endfor %}
</div>
</div>
</div>
{% else %}
{# AJAX loop #}
<div class="col-12">
<div class="row mb-2">
<div class="col-12">
{% for vote in proposal.votes %}
{% if app.user == vote.user %}
{# ID deleteVote here, which has display:none in the CSS #}
<a href="{{ path('vote_delete',{'slug' : slug, 'proposal' : proposal.id, 'vote' : vote.id}) }}" id="deleteVote" class="btn btn-light ml-1 btn-sm" data-toggle="deleteConfirmation">
<i class="bi bi-trash"></i> {{ 'vote-delete' | trans }}
</a>
{% endif %}
{% endfor %}
</div>
</div>
</div>
{% endif %}
If I split both loops, the AJAX one works only when there is already a vote registered (and the button of the standard loop is already there)
I'm trying to deactivate existing_name fields based on values that selected on action field
I'm using crispy and rendering option for action field from models.py
action = models.CharField(max_length=30,
choices=[('add', 'Add'), ('delete', 'Delete'), ('modify', 'Modify'),
('rename', 'Rename')])
I had check all same question on Stackoverfllow and all suggested to deactivate it by script with changes happen action fields
{% extends "KPI_App/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<form action="" method="post" autocomplete="off" >
{% csrf_token %}
<div class="row">
<div class="col-md-4 id='1'">{{form.action|as_crispy_field}}</div>
</div>
<div class="row">
<div class="row id='existing_name' " >{{form.existing_name|as_crispy_field}}</div>
<div class="row">
<div class="col-md-8">
<button type="submit" class="btn btn-success btn-block btn-lg"><i class="fas fa-database"></i> Submit</button>
</div>
<div class="col-md-4">
<a href="{% url 'KPI_list' %}" class="btn btn-secondary btn-block btn-lg">
<i class="fas fa-stream"></i> Back to List
</a>
</div>
</div>
</form>
{% endblock content %}
<script>
$(document).ready(function(){
$("select[name='action']").on('change',function(){
if($(this).val()=='1'){
$("input[name='existing_name']").prop("disabled",false);
}else{
$("input[name='existing_name']").prop("disabled",true);
}
});
});
</script>
I can't assign id for these class and use it it script
Assuming your form input ids are the django default: id_action and id_existing_name.
function toggleAction() {
if ($('#id_action :selected').text() == "Modify") {
$('#id_existing_name').prop('disabled',false)
}
else {
$('#id_existing_name').prop('disabled',true)
}
}
$(document).on('click change', '#id_action', function() {
toggleAction()
})
$(document).ready(function() {
toggleAction();
});
Problem
I want to the following things but am unable to understand that how can I do the following things.
Firstly, I want that when the page is loaded the defualt values should be displayed from using the value from the combox. For example pack of 1KG is the defualt value so it's price and other values should be updated when the page is loaded.
Secondly, I want that when the product is added to the cart the page is not reloaded or refreshed and a popup is shown that the product is added to the cart.
CODE
Script
$(document).on("change", '.tranactionID', function (event) {
event.preventDefault();
//get closest outer div..
var selector = $(this).closest(".productID")
//find to get required elements..
selector.find('.id_price').text($(this).children(":selected").attr("price"));
selector.find('.price-sale').text($(this).children(":selected").attr("sale_price"));
selector.find('.id_discount').text($(this).children(":selected").attr("discount"));
let id = $(this).find("option:selected").attr('transID');
let Url = `{% url 'cart:cart_add' 0 %}`.replace(0, id);
selector.find("form").attr('action', Url);
});
HTML
{% regroup transaction by productID as ProductList %}
{% for productID in ProductList %}
<div class="col-sm-3 productID" >
<div class="product">
<a href="{% url 'main:product-detail' productID.grouper.id %}" class="img-prod"><img class="img-fluid" src={{productID.grouper.product_image.url}} alt="" height="200px">
<span class="status id_discount">%</span>
<div class="overlay"></div>
</a>
<div class="text py-3 pb-4 px-3 text-center">
<h3>{{productID.grouper}}</h3>
<div class="d-flex">
<div class="pricing">
<p class="price"><span class="mr-2 price-dc id_price">Rs. </span><span class="price-sale">Rs. </span></p>
</div>
</div>
<select class="tranactionID" id="ItemID" style="width: 250px;">
{% for val in productID.list %}
<option transID={{val.id}} price={{val.Price}} discount={{val.discount_percentage}} sale_price={{val.get_sale}} class="price_value" >{{val.AUID}} - {{val.Description}}</option>
{% endfor %}
</select>
<form id='transactionIDValue' class="d-inline" method="post">
{{cart_product_form}}
{% csrf_token %}
<input type="submit" id="Id_submit" class="btn btn-primary shadow px-5 py-2" value="Add To Cart">
<!-- <button type="submit" class="btn btn-primary shadow px-5 py-2">Add to Cart</button> -->
</form>
</div>
</div>
</div>
{% endfor %}
I have a list where a user can save various items in a Django app. Next to each items I have a button that can be used to delete this item.
Items are stored in a database and are displayed with a for loop. The problem is whatever button I press, the first one is selected and deleted.
I am new to JavaScript but I do understand that my issue is coming from my var.product selector because .val() returns the first element that matches ('.substitut').
I have tried to tweak a little with $(this) but with no luck...
How can I select each product in each button individually?
My HTML:
{% extends 'finder/base.html' %}
{% block content %}
<header class="masthead" id='fav_list'>
<div class="col-lg-12">
<hr>
<h2 class="intro-text text-center" style="color: beige;">Bienvenue sur ton compte {{ user }}</h2>
<hr class="divider1 my-4" />
<div class='w-75 mx-auto row d-flex justify-content-around mb-3'>
<h3 class="intro-text text-center account_items" style="color: beige;">Produit recherché</h3>
<h3 class="intro-text text-center account_items" style="color: beige;">Produit de substitut</h3>
</div>
</div>
<div class="w-75 mx-auto container-fluid" style='background-color: transparent;'>
{% for saved in saved_list %}
<div class='row d-flex justify-content-between'>
<div class="card mb-3" style="width: 49%;">
{...}
</div>
<div class="card mb-3" style="width: 49%;">
<div class="row no-gutters">
<div class="col-md-2 my-auto">
<img class="mx-auto d-block " style="width:auto; height:auto; max-width:100px; max-height:100px; "
src="{{ saved.sub_product.picture }}">
</div>
<div class="col-md-9">
<div class="card-body">
<h5 class="card-title"><a href="{% url 'finder:detail' saved.sub_product.id %}"
class="aaccount">{{ saved.sub_product.real_name}}/ {{ saved.sub_product.real_brand }}</a>
</h5>
<img src="/static/finder/img/nutriscore-{{ saved.sub_product.nutrition_grade}}.svg"
style="width:70px;"><br>
</div>
</div>
<div class="col-md-1 my-auto mx-auto">
<form class="form_id" method='post'>{% csrf_token %}
<button class=' btn substitut' value='{{ saved.id }}'><i class='fas fa-trash-alt'></i></button>
</form>
</div>
</div>
</div>
</div>
{% endfor %}
</div>
<div class="pagination">
<nav aria-label="">
<ul class="pager mb-1">
{% if saved_list.has_previous %}
<span>previous</span>
{% endif %}
<span class="current" style='color:white;'>
Page {{ saved_list.number }} de {{ saved_list.paginator.num_pages }}
</span>
{% if saved_list.has_next %}
<span>next</span>
{% endif %}
</ul>
</nav>
</div>
</header>
{% endblock %}
My AJAX:
$(".form_id").on('submit', function(event) {
event.preventDefault();
var product = $('.substitut').val();
console.log(product);
var url = '/register/delete/';
$.ajax({
url: url,
type: "POST",
data:{
'product': product,
'csrfmiddlewaretoken': $('input[name=csrfmiddlewaretoken]').val()
},
datatype:'json',
success: function(data) {
if (data['success'])
console.log(product);
$("#fav_list").load(location.href + " #fav_list > *");
location.reload(true);
}
});
});
You can use onclick event of jquery instead of submit , because as i can see in your code provided you are not submiting your form and inside your submit event you are using ajax so this should work.Change your ajax code like below :
$(".substitut").on('click', function(event) {
event.preventDefault();
//getting current button value which is clicked
var product = $(this).val();
console.log(product);
var url = '/register/delete/';
//put your ajax code here
});
Demo code :
$(".substitut").on('click', function(event) {
event.preventDefault();
//getting current button value which is clicked
var product = $(this).val();
console.log(product);
var url = '/register/delete/';
//put your ajax code here
});
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button class='substitut' value='{{ saved.id}}'><i class='fas fa-trash'></i></button>
<button class='substitut' value='{{ saved.id1}}'><i class='fas fa-trash'></i></button>
<button class='substitut' value='{{ saved.id2}}'><i class='fas fa-trash'></i></button>
Update 1: Alternate solution would be creating a click event on button which will store current reference of button clicked and used that inside your submit event .
Demo code :
var $submit = null;
var $button = $('button');
$(".form_id").on('submit', function(event) {
event.preventDefault();
//getting value of button clicked
var product = $submit.value;
console.log(product);
var url = '/register/delete/';
$.ajax({
url: url,
type: "POST",
data: {
'product': product,
'csrfmiddlewaretoken': $('input[name=csrfmiddlewaretoken]').val()
},
datatype: 'json',
success: function(data) {
if (data['success'])
console.log(product);
$("#fav_list").load(location.href + " #fav_list > *");
}
});
});
//when button is clicked
$button.click(function(event) {
//putting current button clicked reference in variable $submit
$submit = this;
});
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form class="form_id" method='post'>{% csrf_token %}{{ saved.id}}
<button class='substitut' value='{{ saved.id}}'><i class='fas fa-trash'></i></button>
</form>
<form class="form_id" method='post'>{% csrf_token %}{{ saved.id1}}
<button class='substitut' value='{{ saved.id1}}'><i class='fas fa-trash'></i></button>
</form>
I am trying to pass the sets ID to a form when a user clicks on the link. Semantic is being used for the frontend.
When the user is viewing a Class, they see a button that says 'New Survey' as below:
This is the HTML:
<a href="/form/create?set_dropdown={{set.id}}">
<button class="ui labeled icon button green">
<i class="inverted plus icon"></i>
New Survey
</button>
</a>
This is the field that is being targeted:
<div class="ui selection dropdown" id="set_dropdown">
<input type="hidden" id='class_dropdown' name="set">
<i class="dropdown icon"></i>
<div class="default text">Class</div>
<div class="menu">
{% for set in sets %}
<div class="item" data-value="{{ set.id }}">{{ set.name }}</div>
{% endfor %}
</div>
</input>
</div>
Its a dropdown that is dynamically rendered in with Jinja2.
Any help would be appreciated
You can use request.args to retrieve parameter from the url
{{ request.args.get('set_dropdown') }}
use this and set a unique id for the selection drop down:
<div class="ui selection dropdown" id="set_dropdown_{{ request.args.get('set_dropdown') }}">
<input type="hidden" id='class_dropdown' name="set">
<i class="dropdown icon"></i>
<div class="default text">Class</div>
<div class="menu">
{% for set in sets %}
<div class="item" data-value="{{ set.id }}">{{ set.name }}</div>
{% endfor %}
</div>
</input>
</div>