我有这样的表格:
class BillType extends AbstractType
{
/**
* @param FormBuilderInterface $builder
* @param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('user')
->add('numberPlate')
->add('servicesPerformed', CollectionType::class, array(
'label' => false,
'entry_type' => ServicePerformedType::class,
'allow_add' => true,
))
->add('Save', SubmitType::class)
;
/**
* @param OptionsResolver $resolver
*/
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'DefaultBundle\Entity\Bill'
));
}
正在ServicePerformedType
上课:
class ServicePerformedType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('description', TextareaType::class, array('label' => false))
->add('price', TextType::class, array('label' => false))
->add('quantity', TextType::class, array('label' => false));
}
}
此模板用于呈现表单:
{{ form(form) }}
<a href="#">Add service</a>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<script type="text/javascript">
var index = 0;
$('a').on('click', function() {
var prototype = $('#bill_servicesPerformed').data('prototype');
prototype = prototype.replace(/_name_/g, index.toString());
$('#bill_servicesPerformed').html(prototype);
index++;
})
</script>
正如在docs中所说,要获得自定义原型,我应该在模板顶部添加以下行:
{% form_theme form _self %}
{% block _servicesPerformed_entry_widget %}
I WILL WRITE MY CUSTOM PROTOTYPE HERE
{% endblock %}
但是,当我按Add service
时,我没有收到I WILL WRITE MY CUSTOME PROTOTYPE HERE
文字,但description
,fields
和quantity
与ServicePerformedType
相关像以前一样上课..
注意:也许还有其他方法可以自定义表单原型,但我对此感兴趣,所以非常感谢能够以这种方式提供自定义表单原型的解决方案的人,谢谢。
答案 0 :(得分:16)
我必须警告你,定制原型可能有点棘手。如果您更改了FormType
字段,则需要浏览模板并进行相同的更改,否则您的表单将无法呈现。
我喜欢做的是为该特定字段创建自定义模板,然后对其进行适当的自定义。所以,看看你的代码我会做这样的事情:
创建一个页面模板 - 您将用于呈现整个页面的模板,包括表单。
{# MyBundle/Resources/views/myPage.html.twig #}
{% extends "::base.html.twig" %}
{# This will tell the form_theme to apply the
`MyBundle:form/fields/servicePerformed.html.twig` template to your "form" #}
{% form_theme form with [
'MyBundle:form/fields/servicePerformed.html.twig'
] %}
{% block body %}
<div>
{{ form_start(form) }}
{{ form_rest(form) }}
{{ form_end(form) }}
</div>
{% endblock %}
现在您需要创建模板MyBundle/Resources/views/form/fields/servicePerformed.html.twig
。它将仅用于自定义servicePerformed
字段。模板应该看起来像这样
{% macro service_template(fields) %}
<tr>
<td>I WILL WRITE MY CUSTOM PROTOTYPE HERE</td>
</tr>
{% endmacro %}
{#
The block name must match your field name!
You can find it by going into the debug toolbar -> Forms ->
click on your form field and then search for "unique_block_prefix".
Copy that and add "_widget" at the end.
#}
{% block _service_performed_widget %}
<table data-prototype="{{ _self.service_template(form.vars.prototype)|e }}">
{% for fields in form.children %}
{{ _self.service_template(fields) }}
{% endfor %}
</table>
{% endblock %}
我想要注意的是,在字段模板中,我传递了原始原型_self.service_template(form.vars.prototype)
。通过这样做,您可以使用原始字段并在自定义原型中呈现它们。例如这段代码:
{% macro service_template(fields) %}
<tr>
<td>{{ form_widget(fields.description) }}</td>
</tr>
{% endmacro %}
将导致类似以下渲染的原型:
<tr>
<td>
<input type="text" id="service_performed___name___description" name="service[__name__][description]"/>
</td>
</tr>
然后,您可以使用javascript操作它。
我希望这会对你有所帮助。
答案 1 :(得分:4)
实际上,形成以下划线_
开头的主题块与具有特定名称的字段相关。
我的意思是,如果您的主表单BillType
被称为my_form
,则需要执行此操作:
{% form_theme form _self %}
{% block _my_form_servicesPerformed_entry_widget %}
I WILL WRITE MY CUSTOM PROTOTYPE HERE
{% endblock %}
这种方法的问题在于它涉及BillType
的特定迭代。如果您在其他地方使用此表单类型并为其提供不同的名称my_form_2
,则必须添加名为_my_form_2_servicesPerformed_entry_widget
的相同块。
答案 2 :(得分:1)
您呈现表单的模板存在一些问题。第一个是这一行:
prototype = prototype.replace(/_name_/g, index.toString());
正则表达式应为__name__
。
接下来,您正在检索原型,但随后立即覆盖它并替换原型的HTML。我无处可见,实际上任何地方都会将新表单附加到您现有的表单中。此外,由于您只有一串文字,因此replace
无法找到要替换的任何文字__name__
。
你应该发布你的Twig / Javascript的完整范围,这样我们才能真正看到#bill_servicesPerformed
以及你想要做的其他事情。在编写自定义原型之前,您应该使用标准原型来确保您没有任何错误。
答案 3 :(得分:0)
作为一个例子,这是我继续前进的方式。我不知道是否有某些理由不这样做,所以要小心。
<div class="modal-body" id="contactMehtods" data-prototype="
{% filter escape %}
{{ include(':organization/partials:prototype_contact_method.html.twig', { 'contact_method': contact_form.contactMethods.vars.prototype }) }}
{% endfilter %}">
<div class="form-group">
{{ form_label(contact_form.name, null, { 'label_attr': { 'class': 'control-label' }}) }}
{{ form_widget(contact_form.name, {'attr': {'class': 'form-control'}}) }}
</div>
</div>
<div class="form-group">
{{ form_label(contact_method.name, null, { 'label_attr': { 'class': 'col-sm-3 control-label' }}) }}
<div class="col-sm-9">
{{ form_widget(contact_method.name, {'attr': {'class': 'form-control'}}) }}
</div>
</div>
<div class="form-group">
{{ form_label(contact_method.value, null, { 'label_attr': { 'class': 'col-sm-3 control-label' }}) }}
<div class="col-sm-9">
{{ form_widget(contact_method.value, {'attr': {'class': 'form-control'}}) }}
</div>
</div>
但需要注意的是,我猜想javascript需要适应这些变化。
答案 4 :(得分:0)
你可以使用宏,看看下面的例子,即使在Symfony3中它也能正常工作。使用此示例,您也可以格式化您的集合原型。
{% macro widget_prototype(widget, remove_text) %}
{% if widget.vars.prototype %}
{% set form = widget.vars.prototype %}
{% set name = widget.vars.name %}
{% else %}
{% set form = widget %}
{% set name = widget.vars.full_name %}
{% endif %}
<div data-content="{{ name }}" class="panel panel-default">
<div class="section row">
<div class="col-md-12">
<label class="field-label">Skill <span class="text-danger">*</span></label>
<label class="field select">
{{ form_widget(form.skill) }}
<i class="arrow double"></i>
</label>
</div>
</div>
<div data-content="{{ name }}">
<a class="btn-remove" data-related="{{ name }}">{{ remove_text }}</a>
{{ form_widget(form) }}
</div>
</div>
{% endmacro %}
有关更详细的示例,请查看gist。