繁体   English   中英

如何动态更改选择的选项?

[英]How to change options of select dynamically?

我正在尝试使用 Symfony 创建一个应用程序,该应用程序旨在允许用户通过创建、删除和编辑事务来管理他的预算。 我已经创建了我的项目,以及我使用 Doctrine 的实体,现在一切都很好,该项目与 Crud 和数据库完美配合。

但是,我有一个问题,如下图所示,使用表单创建了一个新事务,其中包含以下输入:

一个名字,一个数量,一个类型和一个类别 一个类型要么是借方要么是贷方,类别输入代表交易的用途(工资、账单、购物等)

我的问题是我想动态调整类别选择的选项,具体取决于类型选择的值(例如,如果选择贷方,则显示工资,如果是借方,则选项将是账单和购物)。

我知道最好的方法是使用 AJAX,但我在实现它时遇到了一些问题。 实际上,我已经根据为类型选择设置的值开发了对类别选项的改编(如我所愿,它运行良好),但仅在网页加载时。

现在,我想在使用 AJAX 更改时触发相同的事件,这就是我挣扎的地方......我尝试了一些代码,但每次都没有发生任何变化,即使console.log告诉我代码没有遇到任何问题。 这是我的代码:

模板\事务\new.html.twig

{% extends 'base.html.twig' %}

{% block title %}New Transaction{% endblock %}

{% block body %}
    <h1>Create new Transaction</h1>

    {{ form(form)}}

    <button type="submit" class="btn" formonvalidate>Valider</button>

    <a href="{{ path('app_transaction_index') }}">back to list</a>
{% endblock %}

src\Repository\CategoryRepository.php

<?php

namespace App\Repository;

use App\Entity\Category;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
use App\Entity\Type;

/**
 * @extends ServiceEntityRepository<Category>
 *
 * @method Category|null find($id, $lockMode = null, $lockVersion = null)
 * @method Category|null findOneBy(array $criteria, array $orderBy = null)
 * @method Category[]    findAll()
 * @method Category[]    findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
 */
class CategoryRepository extends ServiceEntityRepository
{
    public function __construct(ManagerRegistry $registry)
    {
        parent::__construct($registry, Category::class);
    }

    public function add(Category $entity, bool $flush = false): void
    {
        $this->getEntityManager()->persist($entity);

        if ($flush) {
            $this->getEntityManager()->flush();
        }
    }

    public function remove(Category $entity, bool $flush = false): void
    {
        $this->getEntityManager()->remove($entity);

        if ($flush) {
            $this->getEntityManager()->flush();
        }
    }

    public function findByTypeOrderedByAscName(Type $type): array
    {
        return $this->createQueryBuilder('c')
            ->andWhere('c.type = :type')
            ->setParameter('type', $type)
            ->orderBy('c.title', 'ASC')
            ->getQuery()
            ->getResult()
        ;
    }
}

src\Form\TransactionType.php

<?php

namespace App\Form;

use App\Entity\Transaction;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormEvent;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use App\Repository\TypeRepository;
use App\Repository\CategoryRepository;

class TransactionType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('name')
            ->add('montant')
            ->add('type')
            ->add('category')
        ;
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'data_class' => Transaction::class,
        ]);
    }
}

src\Controller\TransactionController.php

<?php

namespace App\Controller;

use App\Entity\Transaction;
use App\Form\TransactionType;
use App\Repository\TransactionRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormEvent;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use App\Repository\TypeRepository;
use App\Repository\CategoryRepository;
use Symfony\Component\Validator\Constraints\NotBlank;

#[Route('/transaction')]
class TransactionController extends AbstractController
{
    #[Route('/', name: 'app_transaction_index', methods: ['GET'])]
    public function index(TransactionRepository $transactionRepository): Response
    {
        return $this->render('transaction/index.html.twig', [
            'transactions' => $transactionRepository->findAll(),
        ]);
    }

    #[Route('/new', name: 'app_transaction_new', methods: ['GET', 'POST'])]
    public function new(Request $request, TypeRepository $typeRepository, CategoryRepository $categoryRepository): Response
    {
        $form = $this->createFormBuilder(['type' => $typeRepository->find(0)])
            ->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event) use ($categoryRepository) {
                $type = $event->getData()['type'] ?? null;

                $categories = $type === null ? [] : $categoryRepository->findByTypeOrderedByAscName($type);

                $event->getForm()->add('category', EntityType::class, [
                    'class' => 'App\Entity\Category',
                    'choice_label' => 'title',
                    'choices' => $categories,
                    'disabled' => $type === null,
                    'placeholder' => "Sélectionnez une catégorie",
                    'constraints' => new NotBlank(['message' => 'Sélectionnez une catégorie'])
                ]);
            })
            ->add('name')
            ->add('montant')
            ->add('type', EntityType::class, [
                'class' => 'App\Entity\Type',
                'choice_label' => 'title',
                'placeholder' => "Sélectionnez un type",
                'constraints' => new NotBlank(['message' => 'Sélectionnez un type'])
            ])
            ->getForm();

        return $this->renderForm('transaction/new.html.twig', compact('form'));
    }

    #[Route('/{id}', name: 'app_transaction_show', methods: ['GET'])]
    public function show(Transaction $transaction): Response
    {
        return $this->render('transaction/show.html.twig', [
            'transaction' => $transaction,
        ]);
    }

    #[Route('/{id}/edit', name: 'app_transaction_edit', methods: ['GET', 'POST'])]
    public function edit(Request $request, Transaction $transaction, TransactionRepository $transactionRepository): Response
    {
        $form = $this->createForm(TransactionType::class, $transaction);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $transactionRepository->add($transaction, true);

            return $this->redirectToRoute('app_transaction_index', [], Response::HTTP_SEE_OTHER);
        }

        return $this->renderForm('transaction/edit.html.twig', [
            'transaction' => $transaction,
            'form' => $form,
        ]);
    }

    #[Route('/{id}', name: 'app_transaction_delete', methods: ['POST'])]
    public function delete(Request $request, Transaction $transaction, TransactionRepository $transactionRepository): Response
    {
        if ($this->isCsrfTokenValid('delete'.$transaction->getId(), $request->request->get('_token'))) {
            $transactionRepository->remove($transaction, true);
        }

        return $this->redirectToRoute('app_transaction_index', [], Response::HTTP_SEE_OTHER);
    }
}

尝试 AJAX

$(document).on('change', '#form_type', function() {
    const $type = $('#form_type');
    const $form = $(this).closest('form');
    $.ajax({
        url: $form.attr('action'),
        type: $form.attr('method'),
        data: $form.serializeArray(),
        success: function (html) {
            $('#form_category').replaceWith($(html).find('#form_category'));
        }
    });
});

PS:我还希望在选择类型选择的占位符时禁用类别选择,并在使用类型选择选择值时启用类别选择。

主要代码在 TransactionController.php 的公共函数 new() 中。

您可以通过创建具有不同路由的单独控制器操作来执行此操作,该路由返回所需的选项。 然后,您的 JavaScript 将使用返回的数据重新填充表单中的选项。

笔记:

未测试,需要更多工作! (可能包含语法错误、引用不正确的属性名称等)调整此示例流程以满足您的需求。

将以下内容添加到 src\Controller\TransactionController.php

use Symfony\Component\HttpFoundation\JsonResponse;
#[Route('/type/options', name: 'app_transaction_type_options', methods: ['POST'])]
public function new(Request $request, TypeRepository $typeRepository, CategoryRepository $categoryRepository): Response
{
  // TODO: get the Type entity from post data
  // $type = $typeRepository->findOneBy...(
  return new JsonResponse($categoryRepository->findByTypeOrderedByAscName($type));
}

将输入添加到 templates\transaction\new.html.twig 以获取路径。

    <input type="hidden" id="type_options_path" value="{{ path('app_transaction_type_options') }}">

然后你的 JavaScript 看起来像这样:

$(document).on('change', '#form_type', function() {
    const $type = $('#form_type');
    $.ajax({
        url: $('#type_options_path').val(),
        type: 'POST',
        data: {type: $type.val()},
        success: function (data) {
            const $category = $('#form_category');
            $category.find('option').detach();
            const opts = JSON.parse(data)
            for(const opt of opts){
              $(`<option value="${opt.id}">${opt.name}</option>`).appendTo($category)
            }
        }
    });
});

再会。

提交表单的动态生成

可能出现的另一种情况是,您想要自定义特定于用户提交的数据的表单。 例如,假设您有一个体育聚会的注册表。 一些事件将允许您指定您在该领域的首选位置。 例如,这将是一个选择字段。 然而,可能的选择将取决于每项运动。 足球会有进攻、防守、守门员等……棒球会有投手,但不会有守门员。 您需要正确的选项才能通过验证。

聚会作为实体字段传递给表单。 所以我们可以像这样访问每项运动:

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

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
// ...

class SportMeetupType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('sport', 'entity', array(
                'class'       => 'AppBundle:Sport',
                'empty_value' => '',
            ))
        ;

        $builder->addEventListener(
            FormEvents::PRE_SET_DATA,
            function (FormEvent $event) {
                $form = $event->getForm();

                // this would be your entity, i.e. SportMeetup
                $data = $event->getData();

                $sport = $data->getSport();
                $positions = null === $sport ? array() : $sport->getAvailablePositions();

                $form->add('position', 'entity', array(
                    'class'       => 'AppBundle:Position',
                    'empty_value' => '',
                    'choices'     => $positions,
                ));
            }
        );
    }

    // ...
}

当您构建此表单以首次显示给用户时,此示例可以完美运行。

详情在 - https://symfony-docs-zh-cn.readthedocs.io/cookbook/form/dynamic_form_modification.html#cookbook-form-events-submitted-data

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM