@@ -476,6 +476,7 @@ sport like this::
476
476
// src/Acme/DemoBundle/Form/Type/SportMeetupType.php
477
477
namespace Acme\DemoBundle\Form\Type;
478
478
479
+ use Symfony\Component\Form\AbstractType;
479
480
use Symfony\Component\Form\FormBuilderInterface;
480
481
use Symfony\Component\Form\FormEvent;
481
482
use Symfony\Component\Form\FormEvents;
@@ -486,7 +487,10 @@ sport like this::
486
487
public function buildForm(FormBuilderInterface $builder, array $options)
487
488
{
488
489
$builder
489
- ->add('sport', 'entity', array(...))
490
+ ->add('sport', 'entity', array(
491
+ 'class' => 'AcmeDemoBundle:Sport',
492
+ 'empty_value' => '',
493
+ ))
490
494
;
491
495
492
496
$builder->addEventListener(
@@ -497,12 +501,19 @@ sport like this::
497
501
// this would be your entity, i.e. SportMeetup
498
502
$data = $event->getData();
499
503
500
- $positions = $data->getSport()->getAvailablePositions();
504
+ $sport = $data->getSport();
505
+ $positions = null === $sport ? array() : $sport->getAvailablePositions();
501
506
502
- $form->add('position', 'entity', array('choices' => $positions));
507
+ $form->add('position', 'entity', array(
508
+ 'class' => 'AcmeDemoBundle:Position',
509
+ 'empty_value' => '',
510
+ 'choices' => $positions,
511
+ ));
503
512
}
504
513
);
505
514
}
515
+
516
+ // ...
506
517
}
507
518
508
519
When you're building this form to display to the user for the first time,
@@ -539,21 +550,28 @@ The type would now look like::
539
550
namespace Acme\DemoBundle\Form\Type;
540
551
541
552
// ...
542
- use Acme\DemoBundle\Entity\Sport;
543
553
use Symfony\Component\Form\FormInterface;
554
+ use Acme\DemoBundle\Entity\Sport;
544
555
545
556
class SportMeetupType extends AbstractType
546
557
{
547
558
public function buildForm(FormBuilderInterface $builder, array $options)
548
559
{
549
560
$builder
550
- ->add('sport', 'entity', array(...))
561
+ ->add('sport', 'entity', array(
562
+ 'class' => 'AcmeDemoBundle:Sport',
563
+ 'empty_value' => '',
564
+ ));
551
565
;
552
566
553
- $formModifier = function(FormInterface $form, Sport $sport) {
554
- $positions = $sport->getAvailablePositions();
567
+ $formModifier = function(FormInterface $form, Sport $sport = null ) {
568
+ $positions = null === $sport ? array() : $sport->getAvailablePositions();
555
569
556
- $form->add('position', 'entity', array('choices' => $positions));
570
+ $form->add('position', 'entity', array(
571
+ 'class' => 'AcmeDemoBundle:Position',
572
+ 'empty_value' => '',
573
+ 'choices' => $positions,
574
+ ));
557
575
};
558
576
559
577
$builder->addEventListener(
@@ -579,17 +597,78 @@ The type would now look like::
579
597
}
580
598
);
581
599
}
600
+
601
+ // ...
602
+ }
603
+
604
+ You can see that you need to listen on these two events and have different
605
+ callbacks only because in two different scenarios, the data that you can use is
606
+ available in different events. Other than that, the listeners always perform
607
+ exactly the same things on a given form.
608
+
609
+ One piece that is still missing is the client-side updating of your form after
610
+ the sport is selected. This should be handled by making an AJAX call back to
611
+ your application. Assume that you have a sport meetup creation controller::
612
+
613
+ // src/Acme/DemoBundle/Controller/MeetupController.php
614
+ namespace Acme\DemoBundle\Controller;
615
+
616
+ use Symfony\Bundle\FrameworkBundle\Controller\Controller;
617
+ use Symfony\Component\HttpFoundation\Request;
618
+ use Acme\DemoBundle\Entity\SportMeetup;
619
+ use Acme\DemoBundle\Form\Type\SportMeetupType;
620
+ // ...
621
+
622
+ class MeetupController extends Controller
623
+ {
624
+ public function createAction(Request $request)
625
+ {
626
+ $meetup = new SportMeetup();
627
+ $form = $this->createForm(new SportMeetupType(), $meetup);
628
+ $form->handleRequest($request);
629
+ if ($form->isValid()) {
630
+ // ... save the meetup, redirect etc.
631
+ }
632
+
633
+ return $this->render(
634
+ 'AcmeDemoBundle:Meetup:create.html.twig',
635
+ array('form' => $form->createView())
636
+ );
637
+ }
638
+
639
+ // ...
582
640
}
583
641
584
- You can see that you need to listen on these two events and have different callbacks
585
- only because in two different scenarios, the data that you can use is available in different events.
586
- Other than that, the listeners always perform exactly the same things on a given form.
642
+ The associated template uses some JavaScript to update the ``position `` form
643
+ field according to the current selection in the ``sport `` field:
644
+
645
+ .. configuration-block ::
646
+
647
+ .. code-block :: html+jinja
648
+
649
+ {# src/Acme/DemoBundle/Resources/views/Meetup/create.html.twig #}
650
+ {{ form_start(form) }}
651
+ {{ form_row(form.sport) }} {# <select id="meetup_sport" ... #}
652
+ {{ form_row(form.position) }} {# <select id="meetup_position" ... #}
653
+ {# ... #}
654
+ {{ form_end(form) }}
655
+
656
+ .. include :: /cookbook/form/dynamic_form_modification_ajax_js.rst.inc
657
+
658
+ .. code-block :: html+php
659
+
660
+ <!-- src/Acme/DemoBundle/Resources/views/Meetup/create.html.php -->
661
+ <?php echo $view['form']->start($form) ?>
662
+ <?php echo $view['form']->row($form['sport']) ?> <!-- <select id="meetup_sport" ... -->
663
+ <?php echo $view['form']->row($form['position']) ?> <!-- <select id="meetup_position" ... -->
664
+ <!-- ... -->
665
+ <?php echo $view['form']->end($form) ?>
666
+
667
+ .. include :: /cookbook/form/dynamic_form_modification_ajax_js.rst.inc
587
668
588
- One piece that may still be missing is the client-side updating of your form
589
- after the sport is selected. This should be handled by making an AJAX call
590
- back to your application. In that controller, you can submit your form, but
591
- instead of processing it, simply use the submitted form to render the updated
592
- fields. The response from the AJAX call can then be used to update the view.
669
+ The major benefit of submitting the whole form to just extract the updated
670
+ ``position `` field is that no additional server-side code is needed; all the
671
+ code from above to generate the submitted form can be reused.
593
672
594
673
.. _cookbook-dynamic-form-modification-suppressing-form-validation :
595
674
0 commit comments