DEMO / LiveComponent

Dependent Form Fields

Unleash the power of form events, thanks to LiveComponent and DynamicForms.

// ... use statements hidden - click to show
use App\Form\MealPlannerForm;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Form\FormInterface;
use Symfony\UX\LiveComponent\Attribute\AsLiveComponent;
use Symfony\UX\LiveComponent\ComponentWithFormTrait;
use Symfony\UX\LiveComponent\DefaultActionTrait;

#[AsLiveComponent]
class MealPlanner extends AbstractController
{
    use ComponentWithFormTrait;
    use DefaultActionTrait;

    protected function instantiateForm(): FormInterface
    {
        return $this->createForm(MealPlannerForm::class);
    }
}
{# See src/Form/MealPlannerForm.php for the form magic #}
<div
    {{ attributes }}
>
    {{ form_start(form) }}
        {{ form_row(form.meal) }}
        {{ form_row(form.mainFood) }}
        {% if form.pizzaSize is defined %}
            {{ form_row(form.pizzaSize) }}
        {% endif %}
    {{ form_end(form) }}
</div>
// ... use statements hidden - click to show
use App\Enum\Food;
use App\Enum\Meal;
use App\Enum\PizzaSize;
use App\Model\MealPlan;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\EnumType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfonycasts\DynamicForms\DependentField;
use Symfonycasts\DynamicForms\DynamicFormBuilder;

class MealPlannerForm extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        /**
         * Install DynamicFormBuilder:.
         *
         *    composer require symfonycasts/dynamic-forms
         */
        $builder = new DynamicFormBuilder($builder);

        $builder
            ->add('meal', EnumType::class, [
                'class' => Meal::class,
                'choice_label' => fn (Meal $meal): string => $meal->getReadable(),
                'placeholder' => 'Which meal is it?',
                'autocomplete' => true,
            ])
            // see: https://github.com/SymfonyCasts/dynamic-forms
            ->addDependent('mainFood', 'meal', function (DependentField $field, ?Meal $meal) {
                $field->add(EnumType::class, [
                    'class' => Food::class,
                    'placeholder' => null === $meal ? 'Select a meal first' : sprintf('What\'s for %s?', $meal->getReadable()),
                    'choices' => $meal?->getFoodChoices(),
                    'choice_label' => fn (Food $food): string => $food->getReadable(),
                    'disabled' => null === $meal,
                    'autocomplete' => true,
                ]);
            })
            ->addDependent('pizzaSize', 'mainFood', function (DependentField $field, ?Food $food) {
                if (Food::Pizza !== $food) {
                    return;
                }

                $field->add(EnumType::class, [
                    'class' => PizzaSize::class,
                    'placeholder' => 'What size pizza?',
                    'choice_label' => fn (PizzaSize $pizzaSize): string => $pizzaSize->getReadable(),
                    'required' => true,
                    'autocomplete' => true,
                ]);
            })
        ;
    }

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