Symfony:尝试自定义集合表单原型

时间:2016-02-18 18:27:40

标签: symfony

我有这样的表格:

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文字,但descriptionfieldsquantityServicePerformedType相关像以前一样上课..

注意:也许还有其他方法可以自定义表单原型,但我对此感兴趣,所以非常感谢能够以这种方式提供自定义表单原型的解决方案的人,谢谢。

5 个答案:

答案 0 :(得分:16)

我必须警告你,定制原型可能有点棘手。如果您更改了FormType字段,则需要浏览模板并进行相同的更改,否则您的表单将无法呈现。

我喜欢做的是为该特定字段创建自定义模板,然后对其进行适当的自定义。所以,看看你的代码我会做这样的事情:

  1. 创建一个页面模板 - 您将用于呈现整个页面的模板,包括表单。

    {# 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 %}
    
  2. 现在您需要创建模板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 %}
    
  3. 我想要注意的是,在字段模板中,我传递了原始原型_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