So I have a nested CollectionType of File entities:
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('file', FileType::class, array('label' => 'File', 'required' => false));
}
Now if I display the nested entities like so:
<h3>Files</h3>
<ul style="list-style-type:none;padding-left:0;" class="col-files">
{% for file in form.files %}
{{ form_row(file) }}
{% endfor %}
</ul>
With the prototype:
{% block _omics_experiment_files_entry_row %}
<li class="panel panel-body panel-default">
{{ form_row(form.file) }}
</li>
{% endblock %}
It generates HTML like so (after JS injection):
<li class="panel panel-body panel-default">
<div class="form-group">
<label class="control-label" for="omics_experiment_files_0_file">File</label>
<input type="file" id="omics_experiment_files_0_file" name="omics_experiment[files][0][file]">
</div>
<br>
<button type="button" class="btn btn-danger btn-sm"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span> Remove
</button>
<br>
</li>
And when the delete button is pressed the <li> disappears and on save the file is correctly removed from the parent entity.
However, I don't want to display the form row, I'd rather just display the name of the file:
<h3>Files</h3>
<ul style="list-style-type:none;padding-left:0;" class="col-files">
{% for file in form.files %}
<li class="panel panel-body panel-default">
<p>{{ file.vars.value.name }}</p>
</li>
{% endfor %}
</ul>
Which generates HTML like:
<li class="panel panel-body panel-default">
<p>001ÔùÅÔùÅ.lrtemplate</p>
<br><br>
<button type="button" class="btn btn-danger btn-sm"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span> Remove </button>
<br>
</li>
Which removes itself from the DOM fine, but doesn't actually delete the entity.
The button element in both cases simply removes the parent <li>.
Does anyone know what I need to do so I can have the behaviour of the first example, and the UI of the second?
I've already tried including {{form_row(file)}} inside a hidden/visibility:none div, and this caused the DOM to behave unexpectedly so I don't think that works.
Edit:
I've also tried adding a input:hidden field with the same id and name as the form input, and this didn't work.
So I found a slightly hacky solution, if anyone has anything cleaner I'd love to know:
<h3>Files</h3>
<ul style="list-style-type:none;padding-left:0;" class="col-files">
{% for file in form.files %}
<li class="panel panel-body panel-default">
<p>{{ file.vars.value.name }}</p>
{{ form_widget(file.file , { 'attr': {'style': 'display: none;'} }) }}
</li>
{% endfor %}
</ul>
While display: none doesn't work for the entire form_row, it does work for the individual form_widget and this is enough for Symfony to delete the file.
Related
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.
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 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>
I'm creating a blog with python and django. Most of it has been fine up until i've just tried to create the footer. The footer display's fine on the home page but when you click into the blog post the footer gets constrained by the content container and row div class.
When you look at this in firefox dev inspector and DOM it's showing that my footer div is sat within the content container and row, and not in the body. Most things i've usually managed to find the answer for but this is driving me nuts. I think i'm either missing something or i'm not asking the right question.
I don't understand why it's just the footer div that has been put within the content container / row div and nothing else that is effected in the same way.
Is there anyway to amend this without using jquery / jscript and changing the parentElement node?
If i have to amend this with jscript, where exactly do i have to amend it, and with what?
Thanks
.footer {
height: 50px;
background-color: #000000;
color: #ffffff;
padding: 10px;
text-align: center;
clear: both;
}
<div class="content container">
<div class="row">
<div class="col-md-8">
{% block content %}
{% endblock %}
</div>
</div>
</div>
<footer>
<div class="footer">
Copyright © 2017
</div>
</footer>
firefox dev inspector image - sat in row div
firefox dev inspector image - moved to body
This could happen if there are opening tags that don't have closing tags. For example, if your blog looked like this,
{% block content %}
<div id="open_tag_1">
<div id="open_tag_2">
{% endblock %}
The tags that you expect to be closing <div class="content container"> and <div class="row"> will actually be used to close <div id="open_tag_1"> and <div id="open_tag_2">.
<footer> will then be inside <div class="row">.
This is the blog post detail page
{% extends 'blog/base.html' %}
{% block content %}
{% if post.published_date %}
{{ post.published_date }}
by Matt Cheetham
{% else %}
<a class="btn btn-default" href="{% url 'blog.views.post_publish' pk=post.pk %}">Publish</a>
{% endif %}
{% if user.is_authenticated %}
<a class="btn btn-default" href="{% url 'post_edit' pk=post.pk %}"><span class="glyphicon glyphicon-pencil"></span></a>
<a class="btn btn-default" href="{% url 'post_remove' pk=post.pk %}"><span class="glyphicon glyphicon-remove"></span></a>
{% endif %}
<div class="postview">
{{ post.title }}
</div>
<p>{{ post.text|safe }}</p>
<div class="comment">
<div class="date">
<h3>Comments</h3>
<br>
<a class="btn btn-default" href="{% url 'add_comment_to_post' pk=post.pk %}">Add comment</a>
<br>
<br>
{% for comment in post.comments.all %}
{% if user.is_authenticated or comment.approved_comment %}
{{ comment.created_date }}
{% if not comment.approved_comment %}
<a class="btn btn-default" href="{% url 'comment_remove' pk=comment.pk %}"><span class="glyphicon glyphicon-remove"></span></a>
<a class="btn btn-default" href="{% url 'comment_approve' pk=comment.pk %}"><span class="glyphicon glyphicon-ok"></span></a>
{% endif %}
</div>
<strong>{{ comment.author }}</strong>
<p>{{ comment.text|safe }}</p>
</div>
{% endif %}
{% empty %}
<p>No comments here yet...</p>
{% endfor %}
{% endblock %}
I'm working Django CB Views and my template currently has form fields being rendered from an UpdateView. I've split the form and rendered the fields as individual elements. The form has 4 fields, 2 of which are currently under a bootstrap collapse plugin. This plugin is set to hide by default.
<div id="advanced_panel" class="panel-collapse collapse">
But, if the 2 fields are populated, I want to set it to:
<div id="advanced_panel" class="panel-collapse collapse in">
This is what I have so far. My template:
{% block content %}
<form action="" method="post">{% csrf_token %}
{% include 'partials/form_field.html' with field=form.title %}
{% include 'partials/form_field.html' with field=form.body %}
<a class="btn btn-default" data-toggle="collapse" href="#advanced_panel" aria-expanded="true" aria-controls="advanced_panel">Advanced</a>
<div class="panel-collapse collapse" id="advanced_panel">
<div class="card card-block">
{% include 'partials/form_field.html' with field=form.responder_name %}
{% include 'partials/form_field.html' with field=form.response %} #Only show if these two fields exist.
</div>
</div>
Cancel
<input type="submit" class="btn btn-primary" value="Update Review" />
Delete Review
{% endblock %}
How can I do this, maybe using {% if %} and Jquery some how?
{% if form.response and form.responder_name %}
<div id="advanced_panel" class="panel-collapse collapse in">
{% else %}
<div id="advanced_panel" class="panel-collapse collapse">
{% endif %}
or
<div id="advanced_panel" class="panel-collapse collapse
{% if form.response and form.responder_name %} in{% endif %}">
Another option is to pass an additional boolean (e.g. collapse_me) to your template and then use that instead form.* like so:
<div id="advanced_panel" class="panel-collapse collapse{% if collapse_me %} in{% endif %}">
(of course you would need to create additional logic inside your view to set collapse_me=True/collapse_me=False)
That's all I had to add for the class. It works.
class="panel-collapse collapse {% if review.responder_name and review.response %}in{%endif %}"