Closed ghost closed 5 years ago
How to customize a Collection Prototype if the form field is named externalIds
? What is the correct name of the block?
Already tried:
I don't see a block named task
in the section about overriding the collection prototype.
{% block _tasks_entry_widget %}
<tr>
<td>{{ form_widget(task.task) }}</td>
<td>{{ form_widget(task.dueDate) }}</td>
</tr>
{% endblock %}
I mean task.task
and task.dueDate
. Why task
? Where does it come from?
The block itself is named _tasks_entry_widget
because the form field is tasks
.
I don't get it working with a form field named externalId
. What is the correct name of the block?
That because I think the documentation about this should be better. Everybody should easily understand it.
I totally agree with you. I read this documentation again and again, but no way to exploit it.. I'm trying to use Macros to reach my goal, but I am convinced that there is a solution provided by symfony...
I've found out how it works. You need to add the name of the form as prefix.
As example:
{% block _new_sms_recipientNumbers_entry_widget %}
{# ... #}
{% endblock %}
The form type class name is NewSmsType
- that because _new_sms_
is important as prefix.
The collection type is named recipientNumbers
:
$builder->add('recipientNumbers', CollectionType::class);
Now I need the inner variable names. recipientNumber
is not working.
I found a way to get all available variables in a block to understand what is possible:
{% block _new_sms_recipientNumbers_entry_widget %}
{{ dump() }}
{% endblock %}
I am also having difficulties to understand that part of documentation.
The doc shows:
{% form_theme form _self %}
{% block _tasks_entry_widget %}
<tr>
<td>{{ form_widget(form.task) }}</td>
<td>{{ form_widget(form.dueDate) }}</td>
</tr>
{% endblock %}
I don't understand where does {{ form_widget(form.task) }}
and {{ form_widget(form.dueDate) }}
are coming from ?
In my case, I have a field offer_pictures
that I display like this in my template:
{% block content %}
{{ form_start(form) }}
{{ form_widget(form.offer_pictures) }}
{{ form_end(form) }}
{% endblock %}
If I try to customize the placeholder like that:
{% form_theme form _self %}
{% block _offer_offer_pictures_entry_widget %}
{{ form_widget(form.offer_pictures) }}
{% endblock %}
Symfony returns this error : Method "offer_pictures" for object "Symfony\Component\Form\FormView" does not exist in :offer:add.html.twig at line 16
Don't we have access to form fields in the entry_widget block ? As I understand it, that's what the doc suggests.
@Vatvat99 Could you show us your form class and entity fields?
In this block you have access to the fields of the Form type that you embedded in the collection type. offer_pictures
seems to be your collection and not the embedded type.
@Vatvat99 You should try out this code:
{% block _offer_offer_pictures_entry_widget %}
{{ dump() }}
{% endblock %}
This will provide you a lot of information thanks to the VarDumper Component.
@Vatvat99 in the block of your example form
is the entry so you can only access fields of one offer_picture
field. As suggested by @JHGitty {{ dump(form) }}
should help.
@JHGitty if you have a collection field with a name externalIds
, its default block name is the same then you have access to _externalIds_widget
and _externalIds_entry_widget
.
You can customize the block name only (without modifying the field's name) with the option block_name
.
If the entry is also a collection you may have a scenario like this:
class TaskManagerType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options = array())
{
// ...
$builder->add('taskLists', CollectionType::class, array(
'entry_type' => TaskListType::class,
'block_name' => 'task_lists',
));
}
}
class TaskListType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options = array())
{
// ...
$builder->add('tasks', CollectionType::class, array(
'entry_type' => TaskType::class,
));
}
}
class TaskType
{
public function buildForm(FormBuilderInterface $builder, array $options = array())
{
$builder->add('name');
// ...
}
}
Leading to the lines below in a controller:
// ...
$form = $this->createFormBuilder(TaskManagerType::class)
->add('Save', SubmitType::class)
->getForm();
// ...
return $this->render('/form.html.twig', array('form' => $form->createView()));
Then you get the following customizable blocks (*
is standing for "row", "widget", or "label"):
{% block _task_manager_task_lists_* %}{# the collection field of TaskManager #}
{% block _task_manager_task_lists_entry_* %}{# the inner TaskListType #}
{% block _task_manager_task_lists_entry_tasks_* %}{# the collection field of TaskListType #}
{% block _task_manager_task_lists_entry_tasks_entry_* %}{# the inner TaskType #}
{% block _task_manager_task_lists_entry_tasks_entry_name_* %}{# the field of TaskType #}
Hope that helps.
@HeahDude Do you like to optimize the documentation about this?
@JHGitty Do you mean if I'm in favor of some changes or if I should try to send a PR myself?
First, definitely yes, the example provided in http://symfony.com/doc/master/cookbook/form/form_customization.html#how-to-customize-a-collection-prototype looks clearly inconsistent.
I think there is already some work in progress in #6321. Maybe you should give some feedback there to get it moving forward.
@JHGitty @HeahDude @xabbuh Thanks for your help, and sorry for taking time to respond. Indeed, as xabbuh said, I can only access to the fields of the FormType, not the FormType itself.
I think it is important to note that "entry" refers to a numeric key of a collection element.
I have a form named 'contact_details'. It has a collection named 'telephones'. Looking at form html source, I have seen that particular field has an ID: 'contact_details_telephones_0_number'. But, to override the field, one would need to refer to it as:
{% block _contact_details_telephones_entry_number_widget %} ... {% endblock %}
@djuro THANK YOU!!! There is ZERO chance I ever would have stumbled upon what my block should be named from the documentation. Looking at what ID symfony gives a collection item is THE way to do this.
Had the same issue here: https://github.com/ninsuo/symfony-collection/issues/50 and wrote a short post on how I solved it, also used the ID of the DIV, maybe this helps someone else 😄
I had the same issue - tried every prefix I could think of - ultimately it boiled down to missing the {% form_theme form _self %}
immediately before the block override :)
For those wondering how you can KNOW the prefix instead of guessing it, this SO answer tells that you need to go _into the debug toolbar -> Forms -> click on your form field and then search for "unique_blockprefix".
Then it's just a piece of cake provided you read the awesome comments above.
Hi, I am the newbie of symfony and I have read above comments but i still can't find the way out. So i have created the sample one after i spent a half day on it as below:
class EventBackCreateType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options = array())
{
// ...
$builder->add('eventDateTimes', CollectionType::class, array(
'type' => new EventDateTimeType(),
'allow_add' => true,
'allow_delete' => true,
'by_reference' => false
));
}
}
class EventDateTimeType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options = array())
{
parent::buildForm($builder, $options);
$builder->add('eDate', TextType::class, array(
'label' => 'Date',
'attr' => array(
'class' => 'datepicker'
)
));
$builder->add('eTimeFrom', TextType::class, array(
'label' => 'From Time',
'attr' => array(
'class' => 'timepicker'
)
));
$builder->add('eTimeTo', TextType::class, array(
'label' => 'To Time',
'attr' => array(
'class' => 'timepicker'
)
));
}
}
{% extends 'CoreBundle:Crud:edit.html.twig' %}
{# Custom form collection prototype of eventDateTimes #}
{# If you write this '{% block _event_eventDateTimes_entry_widget %}' inside the {% block formbody %}, you will get the error
'Neither the property "eDate" nor one of the methods "eDate()", "geteDate()"/"iseDate()" or "__call()" exist and have public access in class "Symfony\Component\Form\FormView".'
#}
{% block _event_eventDateTimes_entry_widget %}
<td>{{ form_widget(form.eDate) }}</td>
<td>{{ form_widget(form.eTimeFrom) }}</td>
<td>{{ form_widget(form.eTimeTo) }}</td>
{% endblock %}
{% block formbody %}
{#Important : Don't forget import form_theme as below #}
{% form_theme form _self %}
<table class="events-date-time" data-prototype="{{ form_widget(form.eventDateTimes.vars.prototype)|e('html_attr') }}">
</table>
{% endblock %}
My purpose of create this sample just want to help the newbie as me to easy working and understand on customize form collection type. Please kindly feed me back if i have something wrong or still not clear.
Thank you all masters ! (bow)
The simplest solution creates macros in twig, in fact, this is something that does not appear in the documentation. But now I explain.
{% import _self as formMacros %}
{% macro printStepPrototype(defaultStep) %}
<div class="form-group form-float">
<div class="form-line">
{{ form_widget(defaultStep.name) }}
{{ form_label(defaultStep.name) }}
</div>
</div>
{% endmacro %}
At the beginning of your twig template put something like this.
{{ form_start(newStepForm, {'action': path('default_skill_create_trial'), 'method': 'POST', 'attr' : {'id' : 'newStepForm'} }) }}
<div class="row clearfix">
{{ form_errors(newStepForm) }}
<div class="col-sm-12">
<div class="form-group form-float">
<div class="form-line">
{{ form_widget(newStepForm.name) }}
{{ form_label(newStepForm.name) }}
</div>
</div>
{{ form_errors(newStepForm.name) }}
</div>
<div id="defaultSteps" data-prototype="{{ formMacros.printStepPrototype(newStepForm.defaultSteps.vars.prototype)|e('html_attr') }}">
{% for defaultStep in newStepForm.defaultSteps %}
{{ formMacros.printStepPrototype(defaultStep) }}
{% endfor %}
</div>
<button type="submit" class="btn btn-link waves-effect">SAVE</button>
</div>
{{ form_end(newStepForm) }}
Then, below, in the template, your form could be like that, then add the necessary javascript logic, I hope it works for you and do not waste unnecessary hours as it happened to me.
@juanpablomorenomartin I can understand your frustration with the Symfony Form component (and I share that frustration too), but your attitude and comments can't be tolerated. You can say anything you want and you can criticize anything you want ... but you can't be rude or insult to us or our project.
Fully agree with @javiereguiluz I'm really struggling with that, and you help is welcomed, but not with rudeness
I want to apologize for my comments to the truth that day I was very, very angry, but it is not a reason to make comments like that, so I apologize again.
@juanpablomorenomartin we accept your apologies. But please, don't insult us again when you get angry 😠in the future. Thanks!
@BenoitDuffez thank you so much for pointing that out, I've been struggling thinking I was doing it wrong when the only problem was my identifier.
Cheers!
Hello!
I've been struggling with this too and as you guys mentionned, there is absolutely no way to figure all that out straight from the docs, either from https://symfony.com/doc/2.3/cookbook/form/form_customization.html#how-to-customize-a-collection-prototype or https://symfony.com/doc/current/form/form_themes.html#fragment-naming-for-collections
Maybe the solution would be to make a dedicated doc page for working with collections, including this advanced topic with a full example from the controller down to the template?
Maybe an even greater improvement would be to integrate collection handling in Symfony UX since we need boilerplate JavaScript for working with them? Thinking of https://github.com/ruano-a/symfonyCollectionJs among others which I'm working with and very happy of 😋
Hi! For me (symfony/twig-bundle:v4.4.15, twig/twig:v3.0.5), it's worked when I moved the collection block to the separate form_theme. In other cases, it doesn't work in the same template.
http://symfony.com/doc/master/cookbook/form/form_customization.html#how-to-customize-a-collection-prototype
There is a form field named
tasks
. But why is the form field (in the block) namedtask
?For example I have a form field named
externalIds
. Would the form in the block namedexternalId
? If yes: Why? Magic?