I want to keep data that have been entered in fields belonging to a form. I made this form so that the user creates an event. The reason is I included an other form to create a post address, so when the user submit his new address, he retrieves the information previously entered.
Do you have any idea how I can do this ?
(I apologize for all the bootstrap classes in my code.)
event_create.html.twig
{% extends 'base.html.twig' %}
{% block main %}
<div class="events">
<h1 class="display-2 text-center event_form_title">Création d'une nouvelle sortie ou d'une nouvelle activité</h1>
{{ form_start(eventform, {'attr' : {'class' : 'admin_form', 'novalidate': 'novalidate'}}) }}
<div class="d-flex justify-content-center">
<div id="eventform">
<div class="mb-3">
<p>Type</p>
{{ form_widget(eventform.category, {'value' : category.id} ) }}
</div>
<div class="mb-3">
<p>Titre</p>
{{ form_widget(eventform.title) }}
</div>
<div class="mb-3">
<p>Description</p>
{{ form_widget(eventform.description, {'attr' : {'rows' : '3'}} ) }}
</div>
<div class="mb-3 d-none">
<p>Langue à partiquer</p>
{{ form_widget(eventform.spokenlanguage) }}
</div>
<div class="mb-3" id="dateselection">
<p>Date</p>
{{ form_widget(eventform.start.date) }}
</div>
<div class="d-sm-flex justify-content-center">
<div class="d-flex justify-content-center justify-content-sm-end me-sm-3">
<div class="mb-3">
<p>Début</p>
{{ form_widget(eventform.start.time, {'attr' : {'class' : 'time_event'}} ) }}
</div>
</div>
<div class="d-flex justify-content-center justify-content-sm-start">
<div class="mb-3">
<p>Fin</p>
{{ form_widget(eventform.end, {'attr' : {'class' : 'time_event'}} ) }}
</div>
</div>
</div>
<div class="mb-3">
<p>Adresse</p>
{{ form_widget(eventform.address) }}
</div>
<button type="button" onclick="hideAddressButton(); showLocationForm();" class="btn btn-lg btn-outline-secondary w-100 mt-4 mb-5" id="showaddress">Créer une adresse</button>
<div class="d-flex justify-content-center" id="eventsubmit">
{{ form_widget(eventform.save, {'label': "Je valide", 'attr' : {'class' : 'btn btn-lg btn-outline-primary w-100 mb-5'}} ) }}
</div>
<div class="d-flex justify-content-center">
<span class="me-2"><</span> Retour à la liste
</div>
</div>
</div>
</div>
{{ form_end(eventform) }}
<!-- Sélectionner/Ajouter une langue -->
{{ form_start(languageform, {'attr' : {'id' : 'languageform', 'class' : 'mb-3'}}) }}
<p>Langue à pratiquer</p>
{{ form_widget(languageform.code, {'attr': {'onChange': 'selectLanguage();'} }) }}
{{ form_widget(languageform.name, {'attr': {'class': 'd-none'} }) }}
{{ form_widget(languageform.save, {'attr': {'class': 'd-none'} }) }}
{{ form_end(languageform) }}
<ul class="language_names d-none">
{% for language in languages %}
<li class="language_name">{{ language.name }}</li>
{% endfor %}
</ul>
<ul class="language_ids d-none">
{% for language in languages %}
<li class="language_id">{{ language.id }}</li>
{% endfor %}
</ul>
<!-- Créer/Ajouter une adresse -->
{{ form_start(locationform, {'attr' : {'id' : 'locationform'}}) }}
<hr>
<h1 class="display-2 mb-5">Création d'un lieu de rendez-vous</h1>
<div class="d-flex justify-content-center">
<div>
<div class="mb-3">
<p>Nom du lieu</p>
{{ form_widget(locationform.name) }}
</div>
<div class="d-sm-flex justify-content-center">
<div class="d-flex justify-content-center justify-content-sm-end me-sm-3">
<div class="mb-3">
<p>Numéro</p>
{{ form_widget(locationform.number, {'attr' : {'class' : 'numbers_address'}}) }}
</div>
</div>
<div class="d-flex justify-content-center justify-content-sm-start">
<div class="mb-3">
<p>Rue</p>
{{ form_widget(locationform.street, {'attr' : {'class' : 'texts_address'}}) }}
</div>
</div>
</div>
<div class="d-sm-flex justify-content-center">
<div class="d-flex justify-content-center justify-content-sm-end me-sm-3">
<div class="mb-3">
<p>Code postal</p>
{{ form_widget(locationform.zipcode, {'attr' : {'class' : 'numbers_address'}}) }}
</div>
</div>
<div class="d-flex justify-content-center justify-content-sm-start">
<div class="mb-3">
<p>Ville</p>
{{ form_widget(locationform.city, {'attr' : {'class' : 'texts_address'}}) }}
</div>
</div>
</div>
<div class="mb-3">
<p>Grande ville</p>
{{ form_widget(locationform.bigcity) }}
</div>
<button type="button" onclick="hideBigCityButton(); showBigCityForm();" class="btn btn-lg btn-outline-secondary w-100 mt-4" id="showbigcity">Ajouter une grande ville</button>
<div id="bigcity">
{% include "front/create/bigcity.html.twig" %}
</div>
<div class="d-flex justify-content-center my-5">
{{ form_widget(locationform.save, {'label': "Enregistrer une nouvelle adresse", 'attr' : {'class' : 'btn btn-lg btn-outline-primary w-100'}} ) }}
</div>
</div>
</div>
<hr>
{{ form_end(locationform) }}
{% endblock %}
EventController.php
<?php
namespace App\Controller\Front;
class EventController extends AbstractController
{
#[Route('/create/event/category/{id}', name: 'event_create', methods: ['GET', 'POST'])]
public function createEvent(
Request $request,
Category $category,
LanguageRepository $languageRepository,
CountryRepository $countryRepository,
EntityManagerInterface $entityManagerInterface,
UserRepository $userRepository,
LocationRepository $locationRepository
){
$languages = $languageRepository->findAll();
$language = new Language();
$languageform = $this->createForm(EventLanguageType::class, $language);
$languageform->handleRequest($request);
if ($languageform->isSubmitted() && $languageform->isValid()) {
$entityManagerInterface->persist($language);
$entityManagerInterface->flush();
}
$connected = $this->getUser();
$useremail = $connected->getUserIdentifier();
$user = $userRepository->findOneBy(['email' => $useremail]);
$location = new Location();
$locationform = $this->createForm(LocationType::class, $location);
$locationform->handleRequest($request);
if ($locationform->isSubmitted() && $locationform->isValid()) {
$location->setOrganizer($user);
$entityManagerInterface->persist($location);
$entityManagerInterface->flush();
}
$bigcity = new BigCity();
$bigcityform = $this->createForm(BigCityType::class, $bigcity);
$bigcityform->handleRequest($request);
if ($bigcityform->isSubmitted() && $bigcityform->isValid()) {
$entityManagerInterface->persist($bigcity);
$entityManagerInterface->flush();
}
$countries = $countryRepository->findAll();
$country = new Country();
$countryform = $this->createForm(NewCountryType::class, $country);
$countryform->handleRequest($request);
if ($countryform->isSubmitted() && $countryform->isValid()) {
$entityManagerInterface->persist($country);
$entityManagerInterface->flush();
}
$event = new Event();
$eventform = $this->createForm(EventType::class, $event, [
'addresses' => $locationRepository->findBy(['organizer' => $user])
]);
$eventform->handleRequest($request);
if ($eventform->isSubmitted() && $eventform->isValid()) {
$event->setOrganizer($user);
$entityManagerInterface->persist($event);
$entityManagerInterface->flush();
$participation = new Participation();
$participation->setParticipant($user);
$participation->setEvent($event);
$entityManagerInterface->persist($participation);
$entityManagerInterface->flush();
return $this->redirectToRoute('participations');
}
return $this->renderForm('front/event/create.html.twig', [
'eventform' => $eventform,
'languageform' => $languageform,
'languages' => $languages,
'locationform' => $locationform,
'bigcityform' => $bigcityform,
'countryform' => $countryform,
'countries' => $countries,
'category' => $category
]);
}
This is my trial with AJAX, but it does not work :
<script>
// I bring values entered in form and I send it with Ajax
inputs = document.querySelectorAll(["input", "select"]);
function saveDatas(){
let xhr = new XMLHttpRequest();
xhr.open("POST", "https://jsonplaceholder.typicode.com/posts", false);
xhr.setRequestHeader("Content-type", "application/json");
post = {
title: document.getElementById('event_title').value,
description: document.getElementById('event_description').value,
};
xhr.send(JSON.stringify(post));
xhr.onload = function () {
if (xhr.status >= 200 && xhr.status <= 299) {
data = JSON.parse(xhr.response)
}
}
}
for (var i = 0 ; i < inputs.length; i++) {
inputs[i].addEventListener('click', saveDatas);
}
// I retrieve values and I entered them in form fields
languageform = document.getElementById('languageform');
function retrieveDatas(){
let xhr = new XMLHttpRequest();
xhr.open("GET", "https://jsonplaceholder.typicode.com/posts", false);
xhr.send();
xhr.onload = function () {
if (xhr.status >= 200 && xhr.status <= 299) {
posts = JSON.parse(xhr.response)
posts.forEach((post) => {
document.getElementById('event_title').value = post.title;
document.getElementById('event_description').value = post.description;
})
}
}
}
languageform.addEventListener('submit', retrieveDatas);
</script>
I'm not sure to understand. You have a lot of ways to do it. You can use ajax and javascript (Stimulus is very good and documented on SymfonyCast) to avoid to reload page and create your new address. With Stimulus, your data-controller sent an ajax request, create the new address and update its own content.
You can merge your forms (so all data are sent) with two buttons (so you know which button has been push and which action is triggered. Then in your controller, you create the new address and send back all other data.
A third solution is to read this answer. It could help you.
IMO, the first solution is more user friendly.
Related
I'm followed the tutorial to manage the upload of multiple files http://growingcookies.com/easy-multiple-file-upload-in-symfony-using-the-collectiontype-field/
The system for uploading multiple files works fine.
I added a constraint to allow only certain types of files and to set a maximum size.
When this constraint is enabled, the uploaded files are still present in the form but they are actually delete.
The javascript code always displays the files as present when in fact they are no longer present :
The buttons to delete the files are still present ..
Do you know how I can completely delete the form files in case of a constraint violation while displaying the error message ? Or do you have a better way to upload multiple files with constraints in Symfony 4 ?
My twig :
{% extends '#Ticketing/base.html.twig' %}
{% block title %}{{ 'New Ticket'|trans({}, 'TicketingBundle') }}{% endblock %}
{% block header %}<h1>{{ 'New Ticket'|trans({}, 'TicketingBundle') }}</h1>{% endblock %}
{% block form_group_class -%}
col-sm-8
{%- endblock form_group_class %}
{% block main %}
<script type="text/javascript" src="http://code.jquery.com/jquery-latest.min.js"></script>
{% form_theme form 'bootstrap_4_layout.html.twig' _self %}
<div class="box box-danger">
<div class="box-header with-border">
<h3 class="box-title">{{ 'Create a new ticket'|trans({}, 'TicketingBundle') }}</h3>
</div>
{% form_theme form 'bootstrap_3_horizontal_layout.html.twig' _self %}
{{ form_start(form, {'attr': {'class': 'form-horizontal'} }) }}
<div class="box-body">
<div class="hr-line-dashed"></div>
<div id="filesProto" data-prototype="{{ form_widget(form.documents.vars.prototype)|e }}"></div>
<div class="form-group">
<label class="col-sm-2 control-label" for="ticket_form_documents">Pièce-jointe</label>
<div class="col-sm-8" id="filesBox">
{% set pos = 0 %}
{% for doc in form.documents %}
<div class="row">
<div class="col col-xs-1" id="jsRemove{{ pos }}" style="">
<button type="button" class="btn btn-danger" onclick="removeFile($(this));"><i class="fa fa-times" aria-hidden="true"></i></button>
</div>
<div class="col col-xs-11" id="jsPreview{{ pos }}">{{ doc.vars.value.name }}</div>
<div style="display:none">
{{ form_widget(doc) }}
</div>
</div>
{% set pos = pos + 1 %}
{% endfor %}
</div>
</div>
</div>
<!-- /.box-body -->
<div class="box-footer">
<div class="col-md-offset-2 col-sm-8">
<button id="dropbutton" class="btn bg-ticketing btn-flat form-control" type="submit">
{{ 'Submit the ticket'|trans({}, 'TicketingBundle') }}
</button>
</div>
</div>
<!-- /.box-footer -->
{{ form_end(form) }}
</div>
<script>
var fileCount = '{{ form.documents|length }}';
var removeButton = "<button type='button' class='btn btn-danger btn-xs' onclick='removeFile($(this));'><i class='fa fa-times' aria-hidden='true'></i></button>";
function removeFile(ob)
{
ob.parent().parent().remove();
}
function createAddFile(fileCount)
{
// grab the prototype template
var newWidget = $("#filesProto").attr('data-prototype');
// replace the "__name__" used in the id and name of the prototype
newWidget = newWidget.replace(/__name__/g, fileCount);
newWidget = "<div style='display:none'>" + newWidget + "</div>";
hideStuff = "";
hideStuff += "<div class='col col-xs-1' id='jsRemove" + fileCount + "' style='display: none;'>";
hideStuff += removeButton;
hideStuff += "</div>";
hideStuff += "<div class='col col-xs-11' id='jsPreview" + fileCount + "'>";
hideStuff += "</div>";
hideStuff += "<div class='col-sm-8'>";
hideStuff += "<button type='button' id='jsBtnUpload" + fileCount + "' class='btn btn-default'>";
hideStuff += "<i class='fa fa-plus'></i> {{ 'Pièce-jointe' | trans }}";
hideStuff += "</button>";
hideStuff += "</div>";
$("#filesBox").append("<div class='form-group'>" + hideStuff + newWidget + "</div>");
// On click => Simulate file behaviour
$("#jsBtnUpload" + fileCount).on('click', function(e){
$('#ticket_form_documents_' + fileCount + '_file').trigger('click');
});
// Once the file is added
$('#ticket_form_documents_' + fileCount + '_file').on('change', function() {
// Show its name
fileName = $(this).prop('files')[0].name;
$("#jsPreview" + fileCount).append(fileName);
// Hide the add file button
$("#jsBtnUpload" + fileCount).hide();
// Show the remove file button
$("#jsRemove" + fileCount).show();
// Create another instance of add file button and company
createAddFile(parseInt(fileCount)+1);
});
}
$(document).ready(function(){
createAddFile(fileCount);
fileCount++;
});
</script>
{% endblock %}
The code is available here: https://github.com/oanalivia/Multiple-File-Upload-in-Symfony-using-the-CollectionType-Field
one banale solution would be to adapt the output and just filter the docs out, that don't have a value.
{% for doc in form.documents %}
{% if doc.vars.value %} {### <-- new 1/2 ###}
<div class="row">
<div class="col col-xs-1" id="jsRemove{{ pos }}" style="">
<button type="button" class="btn btn-danger" onclick="removeFile($(this));"><i class="fa fa-times" aria-hidden="true"></i></button>
</div>
<div class="col col-xs-11" id="jsPreview{{ pos }}">{{ doc.vars.value.name }}</div>
<div style="display:none">
{{ form_widget(doc) }}
</div>
</div>
{% set pos = pos + 1 %}
{% endif %} {### <-- new 2/2 ###}
{% endfor %}
if you're using a fairly new version of twig (>= 2.10, I believe), you can also only modify the first line of that section:
{% for doc in form.documents|filter(doc => doc.vars.value) %}
I'm new to Symfony, I got a page in where I have a list of articles, and I created a form above it that allow me to filter the results of my query :
The form works fine, but when I click on the 2nd page of my paginator table, the form reset, so my filter is reset too. What I want is that we can filter results and be able to paginate the table, with the same filter, and that the form keep his data.
Here's my code right now :
Controller :
public function listeAction(Request $request, $page) {
if ($page < 1 && strlen($page) != 0) {
throw new NotFoundHttpException('Page "' . $page . '" inexistante.');
} else if (strlen($page) == 0) {
$page = 1;
}
$nbPerPage = 5;
$form = $this->createForm(ArticleRechercheType::class);
$form->setData(array('rds' => true));
//if we use filters
if ($request->isMethod('POST') && $form->handleRequest($request)->isValid()) {
$data = $form->getData();
$form = $this->createForm(ArticleRechercheType::class, $data);
if ($data['famille'] != null) {
$data['famille'] = $data['famille']->getId();
}
$listArticles = $this->getDoctrine()
->getManager()
->getRepository('GRBackOfficeBundle:Article')
->getFilteredArticles($page, $nbPerPage, $data);
} else {
$data = $form->getData();
$form = $this->createForm(ArticleRechercheType::class, $data);
$listArticles = $this->getDoctrine()
->getManager()
->getRepository('GRBackOfficeBundle:Article')
->getArticles($page, $nbPerPage);
}
$nbPages = ceil(count($listArticles) / $nbPerPage);
if ($nbPages != 0 && $page > $nbPages) {
throw new NotFoundHttpException('Page "' . $page . '" inexistante.');
}
if ($nbPages == 0) {
$nbPages = 1;
}
return $this->render('GRBackOfficeBundle:Article:liste_articles.html.twig', array(
'form' => $form->createView(),
'listArticles' => $listArticles,
'nbPages' => $nbPages,
'page' => $page
));
}
Form :
class ArticleRechercheType extends AbstractType {
public function buildForm(FormBuilderInterface $builder, array $options) {
$builder->add('famille', EntityType::class, array(
'class' => 'GRBackOfficeBundle:Famille',
'choice_label' => 'nom',
'required' => false
))
->add('rds', CheckboxType::class, array('required' => false))
->add('recherche', TextType::class, array('required' => false))
->add('Rechercher', SubmitType::class);
}
public function configureOptions(OptionsResolver $resolver) {
$resolver->setDefaults(array(
'translation_domain' => false
));
}
}
View :
{% extends "GRBackOfficeBundle::layout.html.twig" %}
{% block title %}
Liste des articles
{% endblock %}
{% block gr_bo_body %}
<h2>Liste des articles</h2>
<div class="row">
<div class="well row">
{{ form_start(form) }}
{{ form_errors(form) }}
<div class="form-group col-md-2">
{{ form_label(form.famille, "Famille d'article", {'label_attr': {'class': 'control-label'}}) }}
{{ form_errors(form.famille) }}
{{ form_widget(form.famille, {'attr': {'class': 'form-control'}}) }}
</div>
<div class="form-group col-md-4">
{{ form_widget(form.rds, {'attr': {'class': ''}}) }}
{{ form_label(form.rds, "Montrer les articles en rupture de stock", {'label_attr': {'class': 'control-label'}}) }}
{{ form_errors(form.rds) }}
</div>
<div class="form-group col-md-3">
{{ form_label(form.recherche, "Recherche", {'label_attr': {'class': 'control-label'}}) }}
{{ form_errors(form.recherche) }}
{{ form_widget(form.recherche, {'attr': {'class': 'form-control'}}) }}
</div>
<div class="form-group col-md-3" style="text-align: center">
{{ form_widget(form.Rechercher, {'attr': {'class': 'btn btn-primary'}}) }}
</div>
{{ form_rest(form) }}
{{ form_end(form) }}
</div>
<div class="well row">
<table class="table table-bordered table-striped" id="listeArticles" style="width: 100%" cellspacing="0">
<thead>
<tr>
<th>Référence client</th>
<th>Référence interne</th>
<th>Famille</th>
<th>Libellé</th>
<th>Alerte</th>
<th>Stock</th>
</tr>
</thead>
<tbody id="bodyListeArticles">
{% for article in listArticles %}
<tr>
<td>{{ article.RefArticle }}</td>
<td>{{ article.RefLogistique }}</td>
<td>{{ article.famille.nom }}</td>
<td>{{ article.libelle }}</td>
<td>{{ article.StockAlerte }}</td>
<td>{{ article.StockActuel }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if nbPages > 1 %}
<ul class="pagination">
{% for p in range(1, nbPages) %}
<li {% if p == page %} class="active"{% endif %}>
{{ p }}
</li>
{% endfor %}
</ul>
{% endif %}
</div>
</div>
{% endblock %}
If anyone have an idea of how I can do that, that will be appreciated
You can use a bundle (like the knp paginator bundle for exemple), or put your args in session. But the bundle will make it simple as it does everything for you, you just have to pass your datas
I build a webpage using the framework Phalcon. I have a form asking for a text, an integer and a date. I'm using datepicker (https://jqueryui.com/datepicker/) to get the date. When I make the insertion in the database this date is not inserted, only the text and the int.
I think it is problem of the format of the date, so I want to validate it.
I can validate the presence of data in the text fields but I don't find any "validator" to validate it is a date.
This is my form class:
<?php
use Phalcon\Forms\Form;
use Phalcon\Forms\Element\Text;
use Phalcon\Forms\Element\Date;
use Phalcon\Forms\Element\Password;
use Phalcon\Validation\Validator\PresenceOf;
use Phalcon\Validation\Validator\Email;
class NuevakeywordForm extends Form{
public function initialize($entity = null, $options = null){
// Trackeable
$trackeable = new Text('trackeable');
$trackeable->setLabel('Palabra Clave');
$trackeable->setFilters(array('striptags', 'string'));
$trackeable->addValidators(array(
new PresenceOf(array(
'message' => 'Palabra clave requerida'
))
));
$this->add($trackeable);
// IdCliente
$idcliente = new Text('idcliente');
$idcliente->setLabel('idcliente');
$idcliente->addValidators(array(
new PresenceOf(array(
'message' => 'IdCliente is required'
))
));
$this->add($idcliente);
// Fecha Límite
$fecha = new Text('fecha_final');
$fecha->setLabel('Fecha Final');
$fecha->addValidators(array(
new PresenceOf(array(
'message' => 'Por favor pon una fecha límite'
))
));
$this->add($fecha);
}
}
Here is the view:
<div class="page-header">
<h2>Registro de Nueva Keyword</h2>
</div>
<script src="https://code.jquery.com/jquery-1.12.4.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<section class="container animated fadeInUp">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<div id="login-wrapper">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">
Registrar Nueva Keyword
</h3>
</div>
<div class="panel-body">
{{ form('admin/nuevakeyword/', 'id': 'nuevakeywordForm', 'onbeforesubmit': 'return false') }}
<fieldset>
<div class="control-group">
{{ form.label('trackeable', ['class': 'control-label']) }}
<div class="controls">
{{ form.render('trackeable', ['class': 'form-control']) }}
</div>
</div>
<div class="control-group">
{{ form.label('idcliente', ['class': 'control-label']) }}
<div class="controls">
{{ form.render('idcliente', ['class': 'form-control']) }}
</div>
</div>
<div class="control-group">
{{ form.label('fecha_final', ['class': 'control-label']) }}
<div id="datepicker" class="controls">
{{ form.render('fecha_final', ['class': 'form-control']) }}
</div>
</div>
<div class="form-actions">
{{ submit_button('Insertar', 'class': 'btn btn-primary', 'onclick': 'return SignUp.validate();') }}
</div>
</fieldset>
</form>
</div>
</div>
</div>
</div>
</div>
</section>
<script type="text/javascript">
$(function() {
$("#fecha_final").datepicker();
});
</script>
Here is the controller:
public function nuevakeywordAction(){
$auth = $this->session->get('auth');
$permiso = $auth['active'];
if($permiso!='A'){return $this->forward('servicios/index');}
$form = new NuevakeywordForm;
if ($this->request->isPost()) {
$trackeable = $this->request->getPost('trackeable', array('string', 'striptags'));
$idcliente = $this->request->getPost('idcliente');
$fecha = $this->request->getPost('fecha');
$keyword = new Keyword();
$keyword->trackeable = $trackeable;
$keyword->cliente_idcliente = $idcliente;
$keyword->fecha_inicial = new Phalcon\Db\RawValue('now()');
$keyword->fecha_final = $fecha;
if ($keyword->save() == false) {
foreach ($keyword->getMessages() as $message) {
$this->flash->error((string) $message);
}
} else {
$this->tag->setDefault('trackeable', '');
$this->tag->setDefault('idcliente', '');
$this->tag->setDefault('fecha', '');
$this->flash->success('Keyword agregada correctamente');
return $this->forward('admin/verkeywords');
}
}
$this->view->form = $form;
}
I'm using MariaDB (the same as MySQL). I'm trying to find an "addValidator" for the date, but maybe is other the problem. Any help would be welcomed.
Use Phalcon\Validation\Validator\Date
https://docs.phalconphp.com/en/3.0.0/api/Phalcon_Validation_Validator_Date.html
I have used the library django-filter and it supports its own filter form by default. I use {{filter.form}} in template to print it out. Is there a way to configure and customize it in my own needs so that i do not take the word "Φίλτρο" but something else?
I faced this issue also, my solution was the following:
filters.py:
from django import forms
from django.utils.translation import ugettext as _
from django.forms.utils import flatatt
from django.contrib.auth.models import User
from .models import Zone, Location, Note, Price, Item, Unit, Category
import django_filters
class PriceFilter(django_filters.FilterSet):
STATUS_CHOICES = (
(0, _('Pending')),
(1, _('Approved')),
(2, _('Deleted')),
)
location = django_filters.ModelChoiceFilter(queryset=Location.objects.all(), widget=forms.Select(attrs={'class': 'form-control form-control-sm'}))
item = django_filters.ModelChoiceFilter(queryset=Item.objects.all(), widget=forms.Select(attrs={'class': 'form-control form-control-sm'}))
user = django_filters.ModelChoiceFilter(queryset=User.objects.all(), widget=forms.Select(attrs={'class': 'form-control form-control-sm'}))
status = django_filters.ChoiceFilter(choices=STATUS_CHOICES, widget=forms.Select(attrs={'class': 'form-control form-control-sm'}))
date = django_filters.DateFromToRangeFilter(widget=django_filters.widgets.RangeWidget(attrs={'class': 'datepicker form-control form-control-sm', 'placeholder': 'YYYY-MM-DD'}))
class Meta:
model = Price
fields = ('location', 'item', 'unit', 'date', 'status', 'user', 'approved', )
views.py:
from django.shortcuts import get_object_or_404, render, redirect
from django.urls import reverse
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from django.views import generic, View
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.db.models import Count
from .models import Zone, Location, Note, Price, Item, Unit, Category
from .forms import NoteForm
from .filters import PriceFilter
...
class IndexView(generic.ListView):
template_name = 'price/index.html'
def get(self, request):
price_list = Price.objects.all().order_by('-date').annotate(num_notes=Count('note'))
price_filter = PriceFilter(request.GET, queryset=price_list)
paginator = Paginator(price_filter.qs, 30)
page = request.GET.get('page')
try:
prices = paginator.page(page)
except PageNotAnInteger:
prices = paginator.page(1)
except EmptyPage:
prices = paginator.page(paginator.num_pages)
index = paginator.page_range.index(prices.number)
max_index = len(paginator.page_range)
start_index = index - 5 if index >= 5 else 0
end_index = index + 5 if index <= max_index - 5 else max_index
page_range = paginator.page_range[start_index:end_index]
return render(request, self.template_name, {
'prices': prices,
'filter': price_filter.form,
'page_range': page_range,
})
index.html:
{% extends "base/base.html" %}
{% load i18n %}
{% load mptt_tags %}
{% block content %}
<div class="card border-light mb-3">
<div class="card-header">{% trans 'Filter' %}</div>
<div class="card-body">
<form method="get">
<div class="form-row">
<div class="form-group col-md-2">
<label for="inputEmail4" class="col-form-label">{% trans 'Item' %}</label>
{{ filter.item }}
</div>
<div class="form-group col-md-2">
<label for="inputEmail4" class="col-form-label">{% trans 'Location' %}</label>
{{ filter.location }}
</div>
<div class="form-group col-md-2">
<label for="inputEmail4" class="col-form-label">{% trans 'Author' %}</label>
{{ filter.user }}
</div>
<div class="form-group col-md-3">
<label for="inputEmail4" class="col-form-label">{% trans 'Date' %}</label>
<div class="input-group mb-3 mb-sm-0">
{{ filter.date }}
</div>
</div>
<div class="form-group col-md-2">
<label for="inputEmail4" class="col-form-label">{% trans 'Status' %}</label>
{{ filter.status }}
</div>
<div class="form-group col-md-1">
<label for="inputEmail4" class="col-form-label">{% trans 'Actions' %}</label>
<button type="submit" class="btn btn-sm btn-primary">{% trans 'Filter' %}</button>
</div>
</div>
</form>
</div>
</div>
You can use filter like Django's forms but it has some specific widgets also. I advice you to check the documentations:
https://django-filter.readthedocs.io/en/develop/ref/filters.html#widget
This word (which is Filter in english) can be configured by using the help_text attribute to your model attributes. I propose to only post the enligsh translations is stackoverflow so that non-greek people will be able to answer.
As an alternative, you may use the FILTERS_HELP_TEXT_FILTER=False setting (http://django-filter.readthedocs.org/en/latest/ref/settings.html) to disable this text (Filter) completely.
I have a list of records that the user needs the option to edit. This is what I've got so far:
{% for mpt in mempassedtest %}
<!-- Modal Windows: Add Tests & Achievements -->
<div id="editTA{{mpt.id}}" class="modal fade">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h4>Edit Test & Achievement</h4>
</div>
<div class="modal-body">
<form action="{{ path('members_edit_personaldetails', {'member' :memid}) }}" method="post" {{ form_enctype(ta) }} id="editFamilyDetails" class="modaledit">
<table class="table modalform table-condensed">
<tbody>
<tr class="hidden">
<th>{{ form_label(ta.id, 'ID*', { 'attr': {'class': 'title'} }) }}</th>
<td>
{{ form_errors(ta.id) }}
{{ form_widget(ta.id, { 'attr': {'class': 'form-control'}}) }}
</td>
</tr>
<tr class="hidden">
<th>{{ form_label(ta.contact, 'Member*', { 'attr': {'class': 'title'} }) }}</th>
<td>
{{ form_errors(ta.contact) }}
{{ form_widget(ta.contact, { 'attr': {'class': 'form-control'}}) }}
</td>
</tr>
<tr>
<th>{{ form_label(ta.testDescription, 'Test*', { 'attr': {'class': 'title'} }) }}</th>
<td>
{{ form_errors(ta.testDescription) }}
{{ form_widget(ta.testDescription, { 'attr': {'class': 'form-control'}}) }}
</td>
</tr>
<tr>
<th>{{ form_label(ta.taken, 'Test Taken*', { 'attr': {'class': 'title'} }) }}</th>
<td>
{{ form_errors(ta.taken) }}
{{ form_widget(ta.taken, { 'attr': {'class': 'form-control'}}) }}
</td>
</tr>
<tr>
<th>{{ form_label(ta.result, 'Result*', { 'attr': {'class': 'title'} }) }}</th>
<td>
{{ form_errors(ta.result) }}
{{ form_widget(ta.result, { 'attr': {'class': 'form-control'}}) }}
</td>
</tr>
</tbody>
</table>
</div>
<div class="modal-footer">
<input type="submit" value="Save Changes" id="savebuttonta" class="btn btn-success" />
Close
{{ form_rest(ta) }}
</form>
</div>
</div>
</div>
</div>
{% endfor %}
And in the Controller I have this:
// Retrieve Passed Tests For That Member
$passedmembertests = $dm->createQuery('
SELECT mt.id, mt.taken, mt.result, mtd.test
FROM InstructorBundle:MapTests mt
LEFT JOIN InstructorBundle:MapTestDescriptions mtd WHERE mtd.id = mt.testDescription
LEFT JOIN InstructorBundle:MapCentreContacts mc WHERE mc.id = mt.contact
WHERE mc.id = :member
AND mt.result = :passed'
)->setParameters(array(
'member' => $memberint,
'passed' => '1'
));
$mempassedtest = $passedmembertests->getResult();
// Get Tests from Entity for Form use
$memberTA = $dm->getRepository('InstructorBundle:MapTests')->find($memberint);
// Generate Form to edit Tests & Achievements
$ta = $this->createForm( new TaType(), $memberTA);
However, while the modal window is generated each time a record is retrieved, the form doesn't display. What am I missing?
EDIT
This is the TaType() code:
<?php
// src/Acme/MembersBundle/Form/Type/TaType.php
// This is to handle forms for the Members Form
namespace Acme\MembersBundle\Form;
use Doctrine\ORM\EntityRepository;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class TaType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('id', 'integer', array('required'=>false));
$builder->add('contact','entity', array('class'=>'Acme\InstructorBundle\Entity\MapCentreContacts', 'property'=>'id'));
$builder->add('testDescription','entity', array('class'=>'Acme\InstructorBundle\Entity\MapTestDescriptions', 'property'=>'test',
'query_builder' => function(EntityRepository $br) {
return $br->createQueryBuilder('mtd')
->where('mtd.active = :active')
->setParameter('active', '1');
}));
$builder->add('taken', 'date', array('years' => range(1998, date('Y')), 'format' => 'dd-MMMM-yyyy'));
$builder->add('result', 'choice', array(
'choices' => array(
'0' => 'Failed',
'1' => 'Passed'
)
));
}
public function getName()
{
return 'ta';
}
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'Acme\InstructorBundle\Entity\MapTests',
'csrf_protection' => false,
'csrf_field_name' => '_token',
// a unique key to help generate the secret token
'intention' => 'task_item',
));
}
}
A form can only be displayed once.
As I see you have the same form and try to display it more than one time (it is inside a for).
Check if the first time you display the fields it works, and the other times you are trying to display it, it should not return any code.
Update: Multiple forms for TAType()
Create a MainType and inside add this
->add("taList","collection",
array(
"type" => new TAType(),
"label" => false,
'options' => array(
"label" =>false,
)
)
)