How to use template form when edit object - symfony

I have a form with an embedded form (collectionType). I made a template. It works when I add a new object. But how to use this template when I edit an object ? Below my code an 2 pictures to illustrate my problem
{% extends 'base.html.twig' %}
{% form_theme form _self %}
{% block _paris_rencontres_row %}
<div class="sports" id="renc-xx" style="background-color: rgba(182, 184, 185, 0.200);">
<div
class="row">
{# <ul class="sports" data-prototype="{{ form_widget(form.vars.prototype)|e('html_attr') }}"></ul> #}
<div class="col-md-3">
{{ form_row(form.vars.prototype.playedAt) }}
</div>
<div class="col-md-3">
{{ form_row(form.vars.prototype.sport) }}
</div>
</div>
<div class="row">
<div class="col-md-3">
{{ form_row(form.vars.prototype.player_1) }}
</div>
<div class="col-md-3">
{{ form_row(form.vars.prototype.player_2) }}
</div>
<div class="col-md-2">
{{ form_row(form.vars.prototype.cote) }}
</div>
<div class="col-md-2">
{{ form_row(form.vars.prototype.resultat) }}
</div>
</div>
<hr>
</div>
{% endblock %}
{% block body %}
{# {{ include('paris/_form.html.twig', {form:form, button: 'Ajouter'}) }} #}
<button type='button' id='btn-add' class='add_sport_link btn btn-primary'>Ajouter paris</button>
{{ form_start(form) }}
<div class="paris">
<div class="row">
<div class="col-md-4">
{{ form_row(form.mise) }}
</div>
<div class="col-md-4">
{{ form_row(form.type) }}
</div>
<div class="col-md-4">
{{ form_row(form.win) }}
</div>
</div>
<div class="col-md-4 matchs">
{% if form.rencontres|length > 0 %}
{% for index in 0..form.rencontres|length - 1 %}
{{ dump(index) }}
{{ form_row(form.rencontres[index]) }}
{% endfor %}
{% else %}
{{ form_row(form.rencontres) }}
{% endif %}
</div>
</div>
{{ form_end(form) }}
{% endblock %}
add (form.rencontres|length == 0):
enter image description here
edit (form.rencontres|length > 0):
enter image description here
Regards
PS: Sorry for my english
EDIT: My controller
#NicoHaase I dont think the problem is in the code.
I want just display my form template when I edit an object as when I add an object
`code
/**
* #Route("/paris/add", name="paris_add")
*/
public function add(Request $request)
{
$em = $this->getDoctrine()->getManager();
$paris = new paris();
$form = $this->createForm(ParisType::class, $paris);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em->persist($paris);
$em->flush();
return $this->redirectToRoute('paris');
}
return $this->render("paris/add.html.twig", [
'form' => $form->createView()
]);
}
/**
* #Route("/paris/{id}", name="paris_edit", requirements={"id":"\d+"})
* #param Paris $paris
* #param Request $request
*/
public function edit(Request $request, Paris $paris)
{
$form = $this->createForm(ParisType::class, $paris);
$form->handleRequest($request);
return $this->render("paris/add.html.twig", [
"paris" => $paris,
"form" => $form->createView(),
]);
}
`

Related

How to modify Bootstrap 4 form validation information presentation in Django?

I am using Django and Bootstrap 4. I want to customize the way form validation information is displayed in a template I am using. When an error occurs in the current template, the invalid input element has a red outline and the error message is displayed as a pop-up. Also, the input elements that have been inputted correctly don't have a green outline, they just remain without an outline.
I'd like to do the following:
display the error message underneath the field when an error occurs
give the green outline to the fields whose input is correct
I tried multiple solutions, including:
this tutorial
this GitHub gist
the Stack Overflow answers from this question
None worked from my case.
My current code:
submit_job_listing.html (the template):
{% extends "base.html" %}
{% block title %} Submit a job {% endblock %}
{% block nav_item_post_a_job %}active{% endblock nav_item_post_a_job %}
{% block head %}
{{ job_listing_form.media.css }}
{% endblock %}
{% block content %}
<h1>Submit a job listing</h1>
<div class="container">
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="row text-center">
<h2> <b>Job listing information</b> </h2>
</div>
<br/>
{% for field in job_listing_form.visible_fields %}
<div class="form-group">
<div class="row">
<p>{{ field.label_tag }}</p>
</div>
<div class="row">
{% if field.help_text %}
<p class="form-text text-muted">{{ field.help_text }}</p>
{% endif %}
</div>
<div class="row">
{{ field }}
</div>
</div>
{% endfor %}
<br/>
<div class="row text-center">
<h2> <b>Employer information</b> </h2>
</div>
<br/>
{% for field in employer_form.visible_fields %}
<div class="form-group">
<div class="row">
<p>{{ field.label_tag }}</p>
</div>
<div class="row">
{% if field.help_text %}
<p class="form-text text-muted">{{ field.help_text }}</p>
{% endif %}
</div>
<div class="row">
{{ field }}
</div>
</div>
{% endfor %}
<input type="submit" value="Submit" class="btn btn-primary" />
</form>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
{{ job_listing_form.media.js }}
</div>
{% endblock %}
views.py (relevant parts):
def submitJobListing(request):
if request.method == "POST":
employer_form = EmployerForm(request.POST, request.FILES)
job_listing_form = JobListingForm(request.POST, request.FILES)
#check if employer with that name already exists
employer_name = str(request.POST.get("name", ""))
try:
employer_with_the_same_name = Employer.objects.get(name=employer_name)
except ObjectDoesNotExist:
employer_with_the_same_name = None
employer = None
if (employer_with_the_same_name != None):
employer = employer_with_the_same_name
if employer == None and employer_form.is_valid() and job_listing_form.is_valid():
employer = employer_form.save()
job_listing = job_listing_form.save(commit=False)
job_listing.employer = employer
job_listing.save()
job_listing_form.save_m2m()
return SimpleTemplateResponse("employers/successful_job_listing_submission.html")
else:
employer_form = EmployerForm()
job_listing_form = JobListingForm()
context = {
"employer_form": employer_form,
"job_listing_form": job_listing_form,
}
return render(request, 'employers/submit_job_listing.html', context)
forms.py (relevant parts):
class EmployerForm(forms.ModelForm):
name = forms.CharField(max_length=100)
#location = forms.CharField(widget=LocationWidget)
short_bio = forms.CharField(widget=forms.Textarea)
website = forms.URLField()
profile_picture = forms.ImageField()
def __init__(self, *args, **kwargs):
super(EmployerForm, self).__init__(*args, **kwargs)
for visible in self.visible_fields():
if isinstance(visible.field.widget, (forms.widgets.TextInput, forms.widgets.Textarea, forms.widgets.URLInput, LocationWidget)):
visible.field.widget.attrs['class'] = 'form-control'
if isinstance(visible.field.widget, forms.widgets.ClearableFileInput):
visible.field.widget.attrs['class'] = 'form-control-file'
class Meta:
model = Employer
fields = ["name", "short_bio", "website", "profile_picture"]
class JobListingForm(forms.ModelForm):
job_title = forms.CharField(max_length=100, help_text="What's the job title?")
job_description = forms.CharField(widget=forms.Textarea)
job_requirements = forms.CharField(widget=forms.Textarea)
what_we_offer = forms.CharField(widget=forms.Textarea)
location = forms.CharField(widget=LocationWidget)
remote = forms.BooleanField()
job_application_url = forms.URLField()
point_of_contact = forms.EmailField()
categories = forms.ModelMultipleChoiceField(
queryset=Category.objects.all(),
widget=forms.CheckboxSelectMultiple,
required=True)
def __init__(self, *args, **kwargs):
super(JobListingForm, self).__init__(*args, **kwargs)
for visible in self.visible_fields():
if isinstance(visible.field.widget, (forms.widgets.TextInput, forms.widgets.Textarea, forms.widgets.URLInput, forms.widgets.EmailInput, LocationWidget)):
visible.field.widget.attrs['class'] = 'form-control'
if isinstance(visible.field.widget, (forms.widgets.CheckboxInput, forms.widgets.CheckboxSelectMultiple)):
visible.field.widget.attrs['class'] = 'form-check'
class Meta:
model = JobListing
fields = ["job_title", "job_description", "job_requirements", "what_we_offer", "location", "remote", "job_application_url", "categories", "point_of_contact"]
Can someone tell me how can I achieve the effects that I want?
Update 1:
I tried adding the code that checks for field.errors, but it doesn't work.
This is the submit_job_listing.html template file:
{% extends "base.html" %}
{% load widget_tweaks %}
{% block title %} Submit a job {% endblock %}
{% comment %} https://stackoverflow.com/questions/50028673/changing-active-class-in-bootstrap-navbar-not-staying-in-inherited-django-templa {% endcomment %}
{% block nav_item_post_a_job %}active{% endblock nav_item_post_a_job %}
{% block head %}
{{ job_listing_form.media.css }}
<!---
<style>
input, select {width: 100%}
</style>
--->
{% endblock %}
{% block content %}
{% comment %}
https://simpleisbetterthancomplex.com/article/2017/08/19/how-to-render-django-form-manually.html
{% endcomment %}
<h1>Submit a job listing</h1>
<div class="container">
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="row text-center">
<h2> <b>Job listing information</b> </h2>
</div>
<br />
<div class="row text-center">
<p> <b>Note: Fields marked with an asterisk (*) are required</b> </p>
</div>
<br/>
{% for field in job_listing_form.visible_fields %}
<div class="form-group">
<div class="row">
<p>{{ field.label_tag }}{{ field.field.required|yesno:"*," }}</p>
</div>
<div class="row">
{% if field.help_text %}
<p class="form-text text-muted">{{ field.help_text }}</p>
{% endif %}
</div>
<div class="row">
{{ field }}
<p> {{ field.errors }} </p>
{% if field.errors %}
<div class="border-bottom-danger">
{{ field.errors }}
</div>
{% endif %}
</div>
</div>
{% endfor %}
<br/>
<div class="row text-center">
<h2> <b>Employer information</b> </h2>
</div>
<br />
<div class="row text-center">
<p> <b>Note: Fields marked with an asterisk (*) are required</b> </p>
</div>
<br/>
{% for field in employer_form.visible_fields %}
<div class="form-group">
<div class="row">
<p>{{ field.label_tag }}{{ field.field.required|yesno:"*," }}</p>
</div>
<div class="row">
{% if field.help_text %}
<p class="form-text text-muted">{{ field.help_text }}</p>
{% endif %}
</div>
<div class="row">
{{ field }}
</div>
</div>
{% endfor %}
<input type="submit" value="Submit" class="btn btn-primary" />
</form>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
{{ job_listing_form.media.js }}
</div>
{% endblock %}
When I just click the submit button without inputting anything, all the inputs have a red outline, but nothing gets printed out as field.errors. I don't know why is that. Why is that?
Did you tried debugging in the template ?
I did something like you want to achieve :
<form method="post"> {% csrf_token %}
{% for field in form %}
<div class="form-group">
{{ field }}
<label class="control-label" for="{{ field.id_for_label }}">{{ field.label }}</label>
{% if field.errors %}
<div class="border-bottom-danger">
{{ field.errors }}
</div>
{% endif %}
</div>
{% endfor %}
<button class="btn btn-block" type="submit">Submit</button>
</form>
Where :
{{ field }} is the input, where you are correctly adding visible.field.widget.attrs['class'] = 'form-control'
Then, errors you want (I guess) are stored in the field, so you could get its with {{ field.errors }} if it has some.
The difference between your code and mine is that I'm using field.errors instead if field.help_text, I hope it can help.
I suggest you to put breakpoint in your template, then analyze your form object. Then you can do whatever you want in your frontend.
If no error : Add class outline-green to your input wrapper (the <div class="row")
If error : Display the error within a div underneath your input
PS : If you want to outline the input, and not the row wrapper, you can do it through css (or better with Sass if you are using it):
.row.outline-green > input{
border: 1px solid green;
}
<div class="row outline-green">
{{ field }}
</div>
{% load widget_tweaks %}
<div class="col-12 col-md-6">
<div class="form-group">
<label for="{{ form.field1.id_for_label }}" class="text-capitalize">{{ form.field1.label|title }}{% if form.field1.field.required %}<span class="text-danger ml-1">*</span>{% endif %}</label>
{% if form.is_bound %}
{% if form.field1.errors %}
{% render_field form.name class="form-control is-invalid" %}
{% for error in form.field1.errors %}
<div class="invalid-feedback">
{{ error }}
</div>
{% endfor %}
{% else %}
{% render_field form.field1 class="form-control is-valid" %}
{% endif %}
{% else %}
{% render_field form.field1 class="form-control" %}
{% endif %}
{% if form.field1.help_text %}
<small class="form-text text-muted">{{ form.field1.help_text }}</small>
{% endif %}
</div>
</div>
</div>

Customize symfony data-prototype for edit form

I know it's a common question but I cannot figure out how to achieve that.
I have a Course entity and a CourseDocument entity.
Course(id, documents, ...)
CourseDocument(id, file, course)
In my Course form:
class CourseType extends AbstractType
{
/**
* #param FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('title', TextType::class, [
'label' => 'course.title',
])
->add('documents', CollectionType::class, array(
'entry_type' => MediaType::class,
'label' => 'course.documents_list',
'entry_options' => array(
'label' => false,
'data_class' => CourseDocument::class,
),
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false,
))
;
}
In the form I want to be able to add as many documents as I want. I set up everything, javascript included. My only issue is the data-prototype that is not what I want.
{% import "macros/prototype.html.twig" as prototype %}
{{ form_start(form) }}
<div class="row">
<div class="col-md-6">
<fieldset class="form-group">
{{ form_label(form.documents) }}
<div id="course_documents" class="collection_holder" data-prototype="{{ prototype.tagCollectionItem(form.documents.vars.prototype)|e }}">
{% for widget in form.documents %}
{{ prototype.tagCollectionItem(widget) }}
{% endfor %}
</div>
<button type="button" id="add-document-btn" data-target-collection="#{{ form.documents.vars.id }}" class="btn btn-sm btn-info"><i class="la la-plus"></i> {{ 'course.buttons.add_document' | trans({}, 'labels') }}</button>
</fieldset>
</div>
</div>
{{ form_end(form) }}
I'm generating my prototype with a macro:
{% macro tagCollectionItem(item) %}
<fieldset class="form-group">
<div id="{{ item.vars.id }}">
<div class="custom-file">
{{ form_widget(item.uploadedFile) }}
{{ form_label(item.uploadedFile, item.uploadedFile.vars.label, {'label_attr': {'class': 'custom-file-label'}}) }}
</div>
</div>
</fieldset>
{% endmacro %}
It's working pretty well except for editing. I don't want the input if I already selected a file. But I want the name of the file.
I finally used a macro.
{% macro tagCollectionItem(item) %}
<fieldset class="form-group">
<div id="{{ item.vars.id }}">
{% if item.uploadedFile.vars.file_url or item.uploadedFile.vars.image_url %}
{{ form_errors(item.uploadedFile) }}
{{ form_widget(item.uploadedFile, {'attr': {'hidden': true}}) }}
{% else %}
<div class="custom-file">
{{ form_widget(item.uploadedFile) }}
{{ form_label(item.uploadedFile, item.uploadedFile.vars.label, {'label_attr': {'class': 'custom-file-label'}}) }}
</div>
{% endif %}
</div>
</fieldset>
{% endmacro %}
The twig file for the form
<fieldset class="form-group">
{{ form_label(form.documents) }}
<div id="course_documents" class="collection_holder" data-prototype="{{ prototype.tagCollectionItem(form.documents.vars.prototype)|e }}">
{% for widget in form.documents.children %}
{{ prototype.tagCollectionItem(widget) }}
{% endfor %}
</div>
<button type="button" id="add-document-btn" data-target-collection="#{{ form.documents.vars.id }}" class="btn btn-sm btn-info"><i class="la la-plus"></i> {{ 'course.buttons.add_document' | trans({}, 'labels') }}</button>
</fieldset>

Knp Paginator Bundle route error when using slug

I'd like to implement the Knp Pagination for a table.
It is a page where I edit documents, so my route looks like that:
#Route("/document/edit/{id}/") and depending on the document, there is a different ID.
There are several panels on the page and one of them contains a table of different agencies. For that table I used the knp_paginator:
/**
* #var $paginator \Knp\Component\Pager\Paginator
*/
$paginator = $this->get('knp_paginator');
$agencyTable = $paginator->paginate(
$docAgencies,
$request->query->getId('page', 1),
5
);
the $docAgencies is defined like that so it's no query, but that's the way I need to get the information!
$document = $em->getRepository('DocumentBundle:Document')->findOneByIdInContext($id, $currentuser);
$docAgencies = $document->getAgencies();
When first accessing the page, everything is fine and page one of the table is being shown, but as soon as I try to click on another page of the table I get an error:
Controller "DocumentBundle\Controller\Document\EditController::editAction()" requires that you provide a value for the "$id" argument (because there is no default value or because there is a non optional argument after this one).
and my route looks like this:
http://localhost/sp/web/app_dev.php/document/document/edit?page=3
so the id of the document was just deleted and there's nothing to be accessed.
I tried to add a /{page} to my route like it was recommended in other Stackoverflow questions but this doesn't seem to work for me either.
That's how I tried it:
* #Route("/document/edit/{id}/{pag}", name="documentBundle_edit", requirements={"id" = "\d+", "pag" = "\d+"}, defaults={"id" = 0, "pag":1})
the editAction:
public function editAction(Request $request, $id, $pag) {
..
and within the paginate function:
$paginator = $this->get('knp_paginator');
$agencyTable = $paginator->paginate(
$docAgencies,
$pag,
5
);
any ideas why this is not working?
update twig view
{% block content %}
{{ form_start(form) }}
<div class="row">
{% include 'Form/form_errors_banner.html.twig' %}
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-body">
<legend>{{ 'header.info'|trans }}</legend>
{% include 'DocumentBundle:Panels:DocumentInfo.html.twig' %}
</div>
</div>
<div class="panel panel-default">
<div class="panel-body">
<legend>{{ 'label.comments'|trans }}</legend>
{% include 'AppBundle::CommentTable.html.twig' with {
'entity': document,
'add_comments': true
} %}
</div>
</div>
</div>
<div class="col-md-6">
<div class="panel panel-default">
<div class="panel-body">
<legend>{{ 'label.distribution'|trans }}</legend>
<div id="upload_profile_no">
{% trans_default_domain 'AppBundle' %}
{# CONFIG #}
{% if deleteButton is not defined %}
{% set deleteButton = true %}
{% endif %}
{{ form_label(form.agencies) }}<br>
{{ form_widget(form.agencies) }} <br> <br>
<table class="footable">
{# header fields #}
<thead>
<tr>
<th data-field="iata">{{ 'label.iata'|trans }}</th>
<th data-field="name">{{ 'label.name'|trans }}</th>
<th data-field="city">{{ 'label.city'|trans }}</th>
<th data-field="country">{{ 'label.country'|trans }}</th>
{% if deleteButton %}<th data-field="delete"></th>{% endif %}
</tr>
</thead>
{% block tablebody %}
{# body fields #}
<tbody>
{% for agency in docAgencies %}
<tr>
<td data-title="iata" data-market="{{ agency.market.id }}">{{ agency.id }}</td>
<td data-title="name">{{ agency.account }}</td>
<td data-title="city">{{ agency.cityName }}</td>
<td data-title="country">{{ agency.market.id }}</td>
{% if deleteButton %}
<td data-title="delete">
<button class="btn btn-danger btn-xs btn-remove-row" type="button" data-index="{{ agency.id }}">
<span class="glyphicon glyphicon-minus"></span>
</button>
<button class="btn btn-primary btn-xs btn-undo-remove hidden" type="button" data-index="{{ agency.id }}">
<span class="glyphicon glyphicon-repeat"></span>
</button>
</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
{% endblock %}
</table>
{% if docAgencies.pageCount > 1 %}
<div class="navigation text-center">
{{ knp_pagination_render(docAgencies) }}
</div>
{% endif %}
</div>
<br>
<div class="row">
<div class="col-md-6">
{{ form_row(form.airlines, { 'attr': { 'class': 'form-inline'} }) }}
</div>
<div class="col-md-6">
{{ form_row(form.products, { 'attr': { 'class': 'form-inline'} }) }}
</div>
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-body">
<legend>{{ 'label.attachments'|trans }}</legend>
{% set fileDomain = 'document' %}
{% include 'AppBundle::AttachmentConfig.html.twig' %}
{% include 'AppBundle::DisplayAttachments.html.twig' with {'entity': document} %}
{% include 'AppBundle::FileUpload.html.twig' %}
</div>
</div>
</div>
</div>
<div class="row">
{% include 'AppBundle::HelpSubmitButton.html.twig' %}
</div>
{{ form_end(form) }}
{% include 'DocumentBundle:Action/UploadProfile:modals.html.twig' %}
{% endblock content %}
the part for the pagination is the table with {% for agency in docAgencies %}
**update 2 **
my full controller:
class EditController extends Controller {
/**
* #Route("/document/edit", name="documentBundle_edit2")
* #Route("/document/edit/{id}/{pag}", name="documentBundle_edit", requirements={"id" = "\d+", "pag" = "\d+"}, defaults={"id" = 0, "pag" = 1})
* #Template()
*/
public function editAction(Request $request, $id, $pag) {
$currentuser = $this->container->get('security.token_storage')->getToken()->getUser();
$em = $this->getDoctrine()->getManager();
$document = $em->getRepository('DocumentBundle:Document')->findOneByIdInContext($id, $currentuser);
$allComments = $document->getComments();
$docAgencies = $document->getAgencies();
$statusRepository = $em->getRepository('DocumentBundle:Status');
if($this->get('security.authorization_checker')->isGranted('ROLE_DOCUMENT_EDIT') && $document->getStatus()->getName() == 'Effective' && $document->isActive()
|| $this->get('security.authorization_checker')->isGranted('ROLE_CONTRACT_ADMIN')){
/*
* GET DOCUMENT FROM DB
*/
/*
* CHECK Document
*/
if($document == null)
{
return $this->get('app.error_helper')->throwError('error.404');
}
$form = $this->createForm(DocumentEditType::class, $document);
/*
* CREATE PAGINATION FOR TABLES
*/
/**
* #var $paginator \Knp\Component\Pager\Paginator
*/
$paginator = $this->get('knp_paginator');
$agencyTable = $paginator->paginate(
$docAgencies,
$pag,
5
);
$agencyTable->setTemplate('DocumentBundle::table_pagination.html.twig');
$agencyTable->setUsedRoute('documentBundle_edit');
$agencyTable->setParam('id', $id);
$commentTable = $paginator->paginate(
$allComments,
$pag,
5
);
$commentTable->setTemplate('DocumentBundle::table_pagination.html.twig');
/*
* IF REQUEST IS VALID
*/
$form->handleRequest($request);
if ($form->isValid()) {
$effective = date_format($document->getEffective(), 'Y-m-d');
$expires = date_format($document->getExpires(), 'Y-m-d');
$now = date('Y-m-d');
if($now < $effective) {
$status = $statusRepository->findOneById('3');
} elseif ($now >= $expires) {
$status = $statusRepository->findOneById('2');
} else {
$status = $statusRepository->findOneById('1');
}
$document->setStatus($status);
$em->flush();
$this->addFlash(
'success',
'The document has been edited!'
);
return $this->redirectToRoute('documentBundle_document_list');
}
/*
* DISPLAY Document
*/
return $this->render('DocumentBundle:Document:edit.html.twig', Array(
'document' => $document,
'docAgencies' => $agencyTable,
'comments' => $commentTable,
'form' => $form->createView(),
));
} else {
/*
* THROW ERROR
* If the acting user is not allowed to perform this action
*/
return $this->redirectToRoute('documentBundle_view_document', array(
'id' => $id
));
}
}
}
table_pagination.html.twig
<nav>
<ul class="pagination">
{# Previous #}
{% if startPage == 1 %}
<li class="disabled">
<a href="#" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
{% else %}
<li>
<a href="{{ path(app.request.attributes.get('_route'), {'page': startPage - 1}) }} " aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
{% endif %}
{# Pages #}
{% if pageCount < 12 %}{# less than 12 Pages #}
{% for i in 1..pageCount %}
<li {% if startPage == i %}class="active"{% endif %}>{{i}}</li>
{% endfor %}
{% else %}
{% if startPage < 8 %}{# any current page before 7 #}
{# Pages #}
{% for i in 1..8 %}
<li {% if startPage == i %}class="active"{% endif %}>{{i}}</li>
{% endfor %}
<li class="disabled">...</li>
<li>{{pageCount - 1}}</li>
<li>{{pageCount}}</li>
{% else %}
{% if startPage > pageCount - 7 %}{# any current page in the last 7 #}
{# Pages #}
<li>1</li>
<li>2</li>
<li class="disabled">...</li>
{% for i in 7..0 %}
<li {% if startPage == pageCount - i %}class="active"{% endif %}>{{pageCount - i}}</li>
{% endfor %}
{% else %}{# any other Page #}
<li>1</li>
<li>2</li>
<li class="disabled">...</li>
<li>{{startPage - 2}}</li>
<li>{{startPage - 1}}</li>
<li class="active">{{startPage}}</li>
<li>{{startPage + 1}}</li>
<li>{{startPage + 2}}</li>
<li class="disabled">...</li>
<li>{{pageCount - 1}}</li>
<li>{{pageCount}}</li>
{% endif %}{# any current page in the last 7 #}
{% endif %}{# any current page before 8 #}
{% endif %}{# less than 12 Pages #}
{# Next #}
{% if startPage == pageCount %}
<li class="disabled">
<a href="#" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
{% else %}
<li>
<a href="{{ path(app.request.attributes.get('_route'), {'page': startPage + 1}) }} " aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
{% endif %}
</ul>
</nav>
**update including #ste 's help **
Edit Controller:
* #Route("/document/edit/{id}/{page}", name="documentBundle_edit", requirements={"id" = "\d+", "page" = "\d+"}, defaults={"page" = 1})
* #Template()
*/
public function editAction(Request $request, $id, $page) {
$em = $this->getDoctrine()->getManager();
$document = $em->getRepository('DocumentBundle:Document')->findOneByIdInContext($id, $currentuser);
$allComments = $document->getComments();
$docAgencies = $document->getAgencies();
/*
* CREATE PAGINATION FOR TABLES
*/
/**
* #var $paginator \Knp\Component\Pager\Paginator
*/
$paginator = $this->get('knp_paginator');
$agencyTable = $paginator->paginate(
$docAgencies,
$page,
5
);
$agencyTable->setTemplate('DocumentBundle::table_pagination.html.twig');
$commentTable = $paginator->paginate(
$allComments,
$page,
5
);
$commentTable->setTemplate('DocumentBundle::table_pagination.html.twig');
return $this->render('DocumentBundle:Document:edit.html.twig', Array(
'document' => $document,
'docAgencies' => $agencyTable,
'comments' => $commentTable,
'form' => $form->createView(),
));
}
the pagination template
{# Pagination to be included anywhere. Needs {page, pageCount} #}
<nav>
<ul class="pagination">
{# Previous #}
{% if startPage == 1 %}
<li class="disabled">
<a href="#" aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
{% else %}
<li>
<a href="{{ path(route, query|merge({'page': startPage - 1})) }} " aria-label="Previous">
<span aria-hidden="true">«</span>
</a>
</li>
{% endif %}
{# Pages #}
{% if pageCount < 12 %}{# less than 12 Pages #}
{% for i in 1..pageCount %}
<li {% if startPage == i %}class="active"{% endif %}>{{i}}</li>
{% endfor %}
{% else %}
{% if startPage < 8 %}{# any current page before 7 #}
{# Pages #}
{% for i in 1..8 %}
<li {% if startPage == i %}class="active"{% endif %}>{{i}}</li>
{% endfor %}
<li class="disabled">...</li>
<li>{{pageCount - 1}}</li>
<li>{{pageCount}}</li>
{% else %}
{% if startPage > pageCount - 7 %}{# any current page in the last 7 #}
{# Pages #}
<li>1</li>
<li>2</li>
<li class="disabled">...</li>
{% for i in 7..0 %}
<li {% if startPage == pageCount - i %}class="active"{% endif %}>{{pageCount - i}}</li>
{% endfor %}
{% else %}{# any other Page #}
<li>1</li>
<li>2</li>
<li class="disabled">...</li>
<li>{{startPage - 2}}</li>
<li>{{startPage - 1}}</li>
<li class="active">{{startPage}}</li>
<li>{{startPage + 1}}</li>
<li>{{startPage + 2}}</li>
<li class="disabled">...</li>
<li>{{pageCount - 1}}</li>
<li>{{pageCount}}</li>
{% endif %}{# any current page in the last 7 #}
{% endif %}{# any current page before 8 #}
{% endif %}{# less than 12 Pages #}
{# Next #}
{% if startPage == pageCount %}
<li class="disabled">
<a href="#" aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
{% else %}
<li>
<a href="{{ path(route, query|merge({'page': startPage + 1})) }} " aria-label="Next">
<span aria-hidden="true">»</span>
</a>
</li>
{% endif %}
</ul>
</nav>
and this is how I add the pagination to my template:
{{ knp_pagination_render(docAgencies) }}
It seems that you have a problem in table_pagination.html.twig.
You should replace this:
{{ path(app.request.attributes.get('_route'), {'page': THE_PAGE_YOU_HAVE}) }}
with this:
{{ path(route, query|merge({'pag': THE_PAGE_YOU_HAVE})) }}
or this
{{ path(app.request.attributes.get('_route'), app.request.query.all|merge({'pag': THE_PAGE_YOU_HAVE})) }}
Let me know if it works.
Further, you should remove the default value for id in your route for editAction

How to apply style in data-prototype

I'm trying to apply bootstrap in data-prototype but it is not working.
Below is my twig file
{% block body %}
<div class="container">
<div class="page-header">
<h2>New Company</h2>
</div>
{{ form_start(form) }}
<div class="row">
{{ form_row(form.name) }}
<h3>Employees</h3>
<ul class="employees" data-prototype="{{ form_widget(form.employees.vars.prototype)|e('html_attr') }}">
{% for employee in form.employees %}
<li>
<div class="form-group col-sm-6">
{{ form_row(employee.firstName, {'attr':{'class': 'form-control'}}) }}
</div>
<div class="form-group col-sm-6">
{{ form_row(employee.lastName, {'attr':{'class': 'form-control'}}) }}
</div>
</li>
{% endfor %}
</ul>
<button type="submit" class="btn btn-primary">Save</button>
</div>
{{ form_end(form) }}
</div>
{% endblock %}
Button type is okay but the employee.firstName and employee.lastName are not.
You should create a dedicated form theme for your collection type.
Example:
EmployeeCollectionTypeTheme.html.twig
{% block EmployeeCollectionType_widget %}
<li>
<div class="form-group col-sm-6">
{{ form_row(form.firstName, {'attr':{'class': 'form-control'}}) }}
</div>
<div class="form-group col-sm-6">
{{ form_row(form.lastName, {'attr':{'class': 'form-control'}}) }}
</div>
</li>
{% endblock %}
main.html.twig
{% form_theme form.employees 'AppBundle:Default:EmployeeCollectionTypeTheme.html.twig' %}
<ul class="employees" data-prototype="{{ form_widget(form.employees.vars.prototype)|e('html_attr') }}">
{{ form_widget(form.employees) }}
</ul>
Of course, change form type name to fit with yours (except the form variable in the theme, which is given in the prototype context).
To get further, you can check how I built the collection type for templates in https://twigfiddle.com/ (where you have "Add template" in the left pane), by looking at the theme source code.

How to style Symfony2 form prototype

I have a collection of form in Symfony2. I need to style those forms with some specific div structure, something like this:
<div class="block" style="clear: both;">
{{ form_row(form.name) }}
{{ form_row(form.address) }}
</div>
<div class="block">
{{ form_row(form.website) }}
{{ form_row(form.email) }}
</div>
<div>
<div>
{{ form_row(form.text1) }}
</div>
</div>
As you can see, the structure is not regular and I don't have an idea how to style it in the {% for row in rows %} loop in {% block collection_widget %}. Any ideas how I can style/build form prototype so the form added with javascript will look exact as I want?
I've found the best way is to have separate twig template for the form and then pass the form/prototype into that like..
_form.html.twig
<div class="block" style="clear: both;">
{{ form_row(form.name) }}
{{ form_row(form.address) }}
</div>
<div class="block">
{{ form_row(form.website) }}
{{ form_row(form.email) }}
</div>
<div>
<div>
{{ form_row(form.text1) }}
</div>
</div>
update.html.twig
<ul data-prototype="{{ include('_form.html.twig',
{'form': form.items.vars.prototype })|e }}">
{% for item in form.items %}
<li>{{ include('_form.html.twig', {'form': item }) }}</li>
{% endfor %}
</ul>
This will allow you to pass in your form items prototype into the same template that you are using for your actual form items, meaning that you only have to update one template when things change.
You can do it with a simple for loop in twig, without extending the {% block collection_widget %}:
{% for counter in form.MyCollection %}
<div class="block" style="clear: both;">
{{ form_row(form.MyCollection[loop.index0].name) }}
{{ form_row(form.MyCollection[loop.index0].address) }}
</div>
<div class="block">
{{ form_row(form.MyCollection[loop.index0].website) }}
{{ form_row(form.MyCollection[loop.index0].email) }}
</div>
<div>
<div>
{{ form_row(form.MyCollection[loop.index0].text1) }}
</div>
</div>
{% endfor %}

Resources