@@ -614,22 +614,13 @@ your application. Assume that you have a sport meetup creation controller::
614614 namespace Acme\DemoBundle\Controller;
615615
616616 use Symfony\Bundle\FrameworkBundle\Controller\Controller;
617- use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
618- use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
619617 use Symfony\Component\HttpFoundation\Request;
620618 use Acme\DemoBundle\Entity\SportMeetup;
621619 use Acme\DemoBundle\Form\Type\SportMeetupType;
622620 // ...
623621
624- /**
625- * @Route("/meetup")
626- */
627622 class MeetupController extends Controller
628623 {
629- /**
630- * @Route("/create", name="meetup_create")
631- * @Template
632- */
633624 public function createAction(Request $request)
634625 {
635626 $meetup = new SportMeetup();
@@ -639,15 +630,17 @@ your application. Assume that you have a sport meetup creation controller::
639630 // ... save the meetup, redirect etc.
640631 }
641632
642- return array('form' => $form->createView());
633+ return $this->render(
634+ 'AcmeDemoBundle:Meetup:create.html.twig',
635+ array('form' => $form->createView())
636+ );
643637 }
644638
645639 // ...
646640 }
647641
648642The associated template uses some JavaScript to update the ``position `` form
649- field according to the current selection in the ``sport `` field. To ease things
650- it makes use of `jQuery `_ library and the `FOSJsRoutingBundle `_:
643+ field according to the current selection in the ``sport `` field:
651644
652645..configuration-block ::
653646
@@ -660,31 +653,7 @@ it makes use of `jQuery`_ library and the `FOSJsRoutingBundle`_:
660653 {# ... #}
661654 {{ form_end(form) }}
662655
663- {# ... Include jQuery and scripts from FOSJsRoutingBundle ... #}
664- <script>
665- $(function(){
666- // When sport gets selected ...
667- $('#meetup_sport').change(function(){
668- var $position = $('#meetup_position');
669- // Remove current position options except first "empty_value" option
670- $position.find('option:not(:first)').remove();
671- var sportId = $(this).val();
672- if (sportId) {
673- // Issue AJAX call fetching positions for selected sport as JSON
674- $.getJSON(
675- // FOSJsRoutingBundle generates route including selected sport ID
676- Routing.generate('meetup_positions_by_sport', {id: sportId}),
677- function(positions) {
678- // Append fetched positions associated with selected sport
679- $.each(positions, function(key, position){
680- $position.append(new Option(position[1], position[0]));
681- });
682- }
683- );
684- }
685- });
686- });
687- </script>
656+ ..include ::/cookbook/form/dynamic_form_modification_ajax_js.rst.inc
688657
689658 ..code-block ::html+php
690659
@@ -695,72 +664,11 @@ it makes use of `jQuery`_ library and the `FOSJsRoutingBundle`_:
695664 <!-- ... -->
696665 <?php echo $view['form']->end($form) ?>
697666
698- <!-- ... Include jQuery and scripts from FOSJsRoutingBundle ... -->
699- <script>
700- $(function(){
701- // When sport gets selected ...
702- $('#meetup_sport').change(function(){
703- var $position = $('#meetup_position');
704- // Remove current position options except first "empty_value" option
705- $position.find('option:not(:first)').remove();
706- var sportId = $(this).val();
707- if (sportId) {
708- // Issue AJAX call fetching positions for selected sport as JSON
709- $.getJSON(
710- // FOSJsRoutingBundle generates route including selected sport ID
711- Routing.generate('meetup_positions_by_sport', {id: sportId}),
712- function(positions) {
713- // Append fetched positions associated with selected sport
714- $.each(positions, function(key, position){
715- $position.append(new Option(position[1], position[0]));
716- });
717- }
718- );
719- }
720- });
721- });
722- </script>
723-
724- The last piece is implementing a controller for the
725- ``meetup_positions_by_sport `` route returning the positions as JSON according
726- to the currently selected sport. To ease things again the controller makes use
727- of the:doc: `@ParamConverter </bundles/SensioFrameworkExtraBundle/annotations/converters >`
728- listener to convert the submitted sport ID into a ``Sport `` object::
667+ ..include ::/cookbook/form/dynamic_form_modification_ajax_js.rst.inc
729668
730- // src/Acme/DemoBundle/Controller/MeetupController.php
731- namespace Acme\DemoBundle\Controller;
732-
733- // ...
734- use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
735- use Symfony\Component\HttpFoundation\JsonResponse;
736- use Acme\DemoBundle\Entity\Sport;
737-
738- /**
739- * @Route("/meetup")
740- */
741- class MeetupController extends Controller
742- {
743- // ...
744-
745- /**
746- * @Route("/{id}/positions.json", name="meetup_positions_by_sport", options={"expose"=true})
747- */
748- public function positionsBySportAction(Sport $sport)
749- {
750- $result = array();
751- foreach ($sport->getAvailablePositions() as $position) {
752- $result[] = array($position->getId(), $position->getName());
753- }
754-
755- return new JsonResponse($result);
756- }
757- }
758-
759- ..note ::
760-
761- The returned JSON should not be created from an associative array
762- (``$result[$position->getId()] = $position->getName()) ``) as the iterating
763- order in JavaScript is undefined and may vary in different browsers.
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.
764672
765673.. _cookbook-dynamic-form-modification-suppressing-form-validation :
766674
@@ -793,6 +701,3 @@ all of this, use a listener::
793701
794702 By doing this, you may accidentally disable something more than just form
795703 validation, since the ``POST_SUBMIT `` event may have other listeners.
796-
797- .. _`jQuery` :http://jquery.com
798- .. _`FOSJsRoutingBundle` :https://github.com/FriendsOfSymfony/FOSJsRoutingBundle