如何使用带有 craue/CraueFormFlowBundle 步进器的 Symfony4 FormEvent onChange 字段来更新或验证表单?

How to use Symfony4 FormEvent onChange field with craue/CraueFormFlowBundle stepper to update or validate form?

  1. 上下文:

我在 Symfony4 应用程序中使用 craue/CraueFormFlowBundle 包从基本的 SYmfony4 表单生成步进器,它工作正常,直到我尝试引入表单事件以根据表单更改修改我的表单:

How to Dynamically Modify Forms Using Form Events

  1. 代码:

控制器:

**
 * @Route("/user/add", name="add_user")
 * @param Request $request
 */
public function add(Request $request){

    $employe = new Employe();
    $flow = $this->addUserFlow;
    $flow->bind($employe);

    // form of the current step
    $form = $flow->createForm();
    if ($flow->isValid($form)) {
        $flow->saveCurrentStepData($form);

        if ($flow->nextStep()) {
            // form for the next step
            $form = $flow->createForm();
        } else {

            //Persist user
            $this->manager->persist($employe);
            $this->manager->flush();

            //Reset flow to create new user
            $flow->reset();

            //Redirect to new user detail page
            return $this->redirect($this->generateUrl('user_edit', array('id' => $employe->getId())));
        }
    }

    return $this->render('app/users/users/add.html.twig', [
        'form' => $form->createView(),
        'flow' => $flow,
        'employe' => $employe
    ]);
}

AddUserFlow.php

class AddUserFlow extends FormFlow
{
    protected $allowDynamicStepNavigation = true;

    protected function loadStepsConfig()
    {
        return [
            [
                'label' => 'Informations générales',
                'form_type' => UserGeneralType::class,
            ],
            [
                'label' => 'Contact',
                'form_type' => EmployeContactType::class,
            ],
            [
                'label' => 'Contrat',
                'form_type' => EmployeContractType::class,
            ],
            [
                'label' => "Accès a l'application",
                'form_type' => EmployeAccessType::class,
            ]
        ];
    }

}

这是我的第 3 步表单,其中包含 FormEvent:

/**
 * Class ContractType
 * @package App\Form\User\Stepper
 */
class ContractType extends AbstractType
{

    /**
     * @var EntityManagerInterface
     */
    private $manager;

    /**
     * ContractType constructor.
     */
    public function __construct(EntityManagerInterface  $manager)
    {
        $this->manager = $manager;
    }

    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('type', EntityType::class, [
                "class" => TypeContract::class,
                'label'     => 'Type de contrat',
                "required" => true
            ])
            ->add('startDate', DateType::class, [
                'label'     => 'Date de début',
                'widget'    => 'single_text',
                "required"  => false,
            ])
            ->add('endDate', DateType::class, [
                'label'     => 'Date de fin',
                'widget'    => 'single_text',
                "required"  => false,
            ])
            ->add('isInsertion', CheckboxType::class, [
                'label'     => 'Insertion professionnelle',
                "required"  => false,
            ])
            ->add('hourlyRate', MoneyType::class, [
                'label'     => 'Taux horaire',
                "required"  => false,
            ])
        ;

        $formContractTypeModifier = function (FormInterface $form, $contractType = null) {
            if($contractType != null && !$contractType instanceof TypeContract){
                $contractType = $this->manager->getRepository(TypeContract::class)->find($contractType);
            }

            if($contractType != null && $contractType instanceof TypeContract && $contractType->getSlug() === ContractTypeEnum::INTERIM){
                $form->add('company', EntityType::class, [
                    "class" => Company::class,
                    "label"     => "Entreprise",
                    "required"  => true,
                ]);
            }
        };

        // Events listener to modify the form regarding PRE_SET_DATA datas
        $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($formContractTypeModifier){
            /** @var array $data */
            $data = $event->getData();
            /** TypeContract $contractType */
            $contractType = isset($data['type']) ? $data['type'] : null;
            //Add elements to form
            $formContractTypeModifier($event->getForm(), $contractType);
        });

        // Events listener to modify the form regarding PRE_SUBMIT
        $builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) use ($formContractTypeModifier){
            /** @var array $data */
            $data = $event->getData();
            /** TypeContract $contractType */
            $contractType = isset($data['type']) ? $data['type'] : null;
            //Add elements to form
            $formContractTypeModifier($event->getForm(), $contractType);
        });

    }

    /**
     * @param OptionsResolver $resolver
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => null,
        ]);
    }
}

这是我的 add_user.html.twig 观点:

{% if flow.getCurrentStepNumber() == 3 %}
    {% include 'app/users/users/stepper/_step_3.html.twig' %}
{% else %}
    {{ form_errors(form) }}
    {{ form_rest(form) }}
{% endif %}

step_3.html.twig 视图是这样的:

{{ form_row(form.contracts.type) }}

{% if form.contracts.company is defined %}
    {{ form_row(form.contracts.company) }}
{% endif %}

{{ form_row(form.contracts.startDate) }}
{{ form_row(form.contracts.endDate) }}
{{ form_row(form.contracts.hourlyRate) }}
{{ form_row(form.contracts.isInsertion) }}


<script>
    $( document ).ready(function() {
        $('#employe_contract_contracts_type').on('change', function() {
            $.ajax({
                url : $form.attr('action'),
                type: $form.attr('method'),
                data : data,
                success: function(html) {
                    console.log(html);
                    console.log($(html));
                    $('#add_employe_step_3_container').replaceWith($(html).find('#add_employe_step_3_container'));
                }
            });

        });
    });
</script>

  1. 问题:

我的问题是我不确定 CraueFlow 是否按照我在 FormEvent 中使用它的方式工作,我没有找到任何文档。

当我尝试我刚刚在这个问题中提出的方法时,我从 Ajax 调用中得到了一个结果,其中包含我的 Flow 的第一步。结果是 $('#add_employe_step_3_container').replaceWith($(html).find('#add_employe_step_3_container')); 没有在我的容器中加载任何东西,因为它没有找到。

当我进入下一步然后返回到第三步时,新的 company 字段出现在我的第 3 步中,因为全局表单似乎已更新并且 formEvent 已正常工作。

有什么想法或例子吗?

要刷新表单并重新加载错误和控件,我只需要在不调用 $flow->nextStep()

的情况下呈现表单
/**
 * @Route("/user/add", name="user_add")
 */
public function add(Request $request, UserPasswordEncoderInterface $encoder, UniquePasswordResetToken $uniqueToken){

    $employe = new Employe();
    $flow = $this->addUserFlow;
    $flow->bind($employe);

    // form of the current step
    $form = $flow->createForm();
    if ($flow->isValid($form)) {
        $flow->saveCurrentStepData($form);
        if ($request->isXmlHttpRequest()) {
            $form = $flow->createForm();
        } else {

            if ($flow->nextStep()) {
                // form for the next step
                $form = $flow->createForm();
            } else {


                //Persist user
                $this->manager->persist($employe);
                $this->manager->flush();

                //Persist change password user token
                $this->manager->persist($uniqueToken->getToken($employe));
                $this->manager->flush();

                
                //Reset flow to create new user
                $flow->reset();

                //Redirect to new user detail page
                return $this->redirect($this->generateUrl('user_edit_personnal_info', array('id' => $employe->getId())));
            }
        }
    }

    return $this->render('app/users/users/add.html.twig', [
        'form' => $form->createView(),
        'flow' => $flow,
        'employe' => $employe
    ]);
}