动态变化的形式是 Symfony 5

Posted

技术标签:

【中文标题】动态变化的形式是 Symfony 5【英文标题】:Dynamically changing form is Symfony 5 【发布时间】:2021-10-30 06:57:10 【问题描述】:

我正在尝试制作一个根据用户响应进行更改的表单,我有一个 ChoiceType::class 用于第一部分,其中“是”或“否”作为选项。如果用户选择“是”,我希望显示表单的第二部分以得到他们的回复,但如果他们选择“否”,我只想隐藏第二个表单。

这是我目前的表格

public function buildForm(FormBuilderInterface $builder, array $options)

  ->add('attending', ChoiceType::class, [
     'choices' => [
       'yes' => true,
       'no' => false,
     ],
     'attr' => [
        'class' => 'attendanceStatus'
     ],
     'mapped' => false,
     'required' => true,
     'label' => 'Will you be attending?',
     'placeholder' => 'Please make selection',
  ])
  ->add('bringingGuest', ChoiceType::class, [
     'choices' => [
       'yes' => true,
       'no' => false,
     ],
       
         

我将表单包装在一个类中,并为每个表单指定了一个 ID

    <div class="attendance">
      <div id="attendance-status">
         form_label(form.attending) 
         form_errors(form.attending) 
         form_widget(form.attending) 
      </div>
      <div id="guest" style="display: none;">
         form_label(form.bringingGuest) 
         form_errors(form.bringingGuest) 
         form_widget(form.bringingGuest) 
      </div>
</div>

我不是最擅长 javascript,但我尝试做这样的 if 语句

if ('.attending' == true) 
 document.getElementById('guest').style.display = 'block';

我在这方面已经有一段时间了,但我似乎无法弄清楚如何正确地做到这一点。我认为这就像为用户选择设置一个事件侦听器,然后在满足条件时仅使用 javascript 显示第二种形式。

【问题讨论】:

【参考方案1】:

就像您可能已经知道的那样,这是一个 javascript 问题。就像您说的那样,您可以使用事件侦听器,例如在值更改时运行函数。这是一个例子:

const someId = document.getElementById('some-id');
const example = document.getElementById('example');

someId.addEventListener('change', doSomething);

function doSomething() 
  if (someId.value === "yes") 
    example.innerhtml = "YES!"
   else 
    example.innerHTML = ""
  
<select name="name" id="some-id">
  <option value="no">no</option>
  <option value="yes">yes</option>
</select>

<div id="example">
</div>

您需要做的就是检查您应该定位哪些 ID 以及您希望在哪个事件上发生什么。要了解有关活动的更多信息,请参阅:https://developer.mozilla.org/en-US/docs/Web/Events。

【讨论】:

但是当表单不是来自 HTML,它来自 Symfony 时,这样的事情如何工作?我不需要调用 Symfony 类而不是 html 元素吗,因为我不相信它们彼此重叠。 您的 php 代码(使用 Symfony)生成 HTML。您可以在浏览器中检查此 HTML,并使用生成的字段的 ID(或将它们包装在另一个元素中)来定位它们。 这需要一个提交按钮还是会在他们从下拉列表中做出选择的那一刻发生? 当你添加一个事件监听器时,你可以监听任何事件,比如改变、聚焦、选择、提交等等……所以不,它不需要提交按钮。跨度> 【参考方案2】:

您可能需要查看 Symfony 文档中的 How to Dynamically Modify Forms Using Form Events 页面。 在您的情况下,它将是 Dynamic Generation for Submitted Forms 部分。

它涉及 2 种类型的事件 - PHP/Symfony 端的表单事件和 JS 主要是“被动”角色来监听元素更改并替换 HTML。

接下来的想法是:

当用户对 HTML 元素执行操作时,向后端发送请求,并根据元素的更改值呈现页面的新状态。在这里,您将获得整页 HTML 作为响应。 然后,只需将页面的相关块替换为您从响应中获得的新呈现的 HTML 片段。

以下是主要部分(从文档复制粘贴):


// src/Form/Type/SportMeetupType.php
namespace App\Form\Type;

use App\Entity\Position;
use App\Entity\Sport;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\FormInterface;
// ...

class SportMeetupType extends AbstractType

    public function buildForm(FormBuilderInterface $builder, array $options): void
    
        $builder
            ->add('sport', EntityType::class, [
                'class' => Sport::class,
                'placeholder' => '',
            ])
        ;

        $formModifier = function (FormInterface $form, Sport $sport = null) 
            $positions = null === $sport ? [] : $sport->getAvailablePositions();

            $form->add('position', EntityType::class, [
                'class' => Position::class,
                'placeholder' => '',
                'choices' => $positions,
            ]);
        ;

        $builder->addEventListener(
            FormEvents::PRE_SET_DATA,
            function (FormEvent $event) use ($formModifier) 
                // this would be your entity, i.e. SportMeetup
                $data = $event->getData();

                $formModifier($event->getForm(), $data->getSport());
            
        );

        $builder->get('sport')->addEventListener(
            FormEvents::POST_SUBMIT,
            function (FormEvent $event) use ($formModifier) 
                // It's important here to fetch $event->getForm()->getData(), as
                // $event->getData() will get you the client data (that is, the ID)
                $sport = $event->getForm()->getData();

                // since we've added the listener to the child, we'll have to pass on
                // the parent to the callback functions!
                $formModifier($event->getForm()->getParent(), $sport);
            
        );
    

    // ...

在 JS 中:


# templates/meetup/create.html.twig #
 form_start(form) 
     form_row(form.sport)     # <select id="meetup_sport" ... #
     form_row(form.position)  # <select id="meetup_position" ... #
    # ... #
 form_end(form) 

<script>
var $sport = $('#meetup_sport');
// When sport gets selected ...
$sport.change(function() 
  // ... retrieve the corresponding form.
  var $form = $(this).closest('form');
  // Simulate form data, but only include the selected sport value.
  var data = ;
  data[$sport.attr('name')] = $sport.val();
  // Submit data via AJAX to the form's action path.
  $.ajax(
    url : $form.attr('action'),
    type: $form.attr('method'),
    data : data,
    success: function(html) 
      // Replace current position field ...
      $('#meetup_position').replaceWith(
        // ... with the returned one from the AJAX response.
        $(html).find('#meetup_position')
      );
      // Position field now displays the appropriate positions.
    
  );
);
</script>

【讨论】:

以上是关于动态变化的形式是 Symfony 5的主要内容,如果未能解决你的问题,请参考以下文章

为啥附加到列表的值会动态变化?

动态刷新机制的业务对象

性能:监听所有表单元素的变化,将所有动态属性与新数据同步

在 Jupyter Notebook 中使用 matplotlib 绘制动态变化的图形

QML 视图中语言的动态变化

Swift iOS 应用程序 - 静态分配具有动态变化行的表属性