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