Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit2533f29

Browse files
committed
[Cookbook][Dynamic Form Modification] Add AJAX sample
1 parent5d4a3a4 commit2533f29

File tree

1 file changed

+136
-15
lines changed

1 file changed

+136
-15
lines changed

‎cookbook/form/dynamic_form_modification.rst

Lines changed: 136 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -486,7 +486,10 @@ sport like this::
486486
public function buildForm(FormBuilderInterface $builder, array $options)
487487
{
488488
$builder
489-
->add('sport', 'entity', array(...))
489+
->add('sport', 'entity', array(
490+
'class' => 'AcmeDemoBundle:Sport',
491+
'empty_value' => '',
492+
));
490493
;
491494

492495
$builder->addEventListener(
@@ -497,12 +500,18 @@ sport like this::
497500
// this would be your entity, i.e. SportMeetup
498501
$data = $event->getData();
499502

500-
$positions = $data->getSport()->getAvailablePositions();
503+
$sport = $data->getSport();
504+
$positions = (null === $sport) ? array() : $sport->getAvailablePositions();
501505

502-
$form->add('position', 'entity', array('choices' => $positions));
506+
$form->add('position', 'entity', array(
507+
'class' => 'AcmeDemoBundle:Position',
508+
'empty_value' => '',
509+
'choices' => $positions,
510+
));
503511
}
504512
);
505513
}
514+
// ...
506515
}
507516

508517
When you're building this form to display to the user for the first time,
@@ -547,13 +556,20 @@ The type would now look like::
547556
public function buildForm(FormBuilderInterface $builder, array $options)
548557
{
549558
$builder
550-
->add('sport', 'entity', array(...))
559+
->add('sport', 'entity', array(
560+
'class' => 'AcmeDemoBundle:Sport',
561+
'empty_value' => '',
562+
));
551563
;
552564

553-
$formModifier = function(FormInterface $form, Sport $sport) {
554-
$positions = $sport->getAvailablePositions();
565+
$formModifier = function(FormInterface $form, Sport $sport = null) {
566+
$positions =(null === $sport) ? array() :$sport->getAvailablePositions();
555567

556-
$form->add('position', 'entity', array('choices' => $positions));
568+
$form->add('position', 'entity', array(
569+
'class' => 'AcmeDemoBundle:Position',
570+
'empty_value' => '',
571+
'choices' => $positions,
572+
));
557573
};
558574

559575
$builder->addEventListener(
@@ -579,17 +595,119 @@ The type would now look like::
579595
}
580596
);
581597
}
598+
// ...
582599
}
583600

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.
601+
You can see that you need to listen on these two events and have different
602+
callbacks only because in two different scenarios, the data that you can use is
603+
available in different events. Other than that, the listeners always perform
604+
exactly the same things on a given form.
605+
606+
One piece that is still missing is the client-side updating of your form after
607+
the sport is selected. This should be handled by making an AJAX call back to
608+
your application. Assume that you have a sport meetup creation controller::
609+
610+
// src/Acme/DemoBundle/Controller/MeetupController.php
611+
// ...
612+
613+
/**
614+
* @Route("/meetup")
615+
*/
616+
class MeetupController extends Controller
617+
{
618+
/**
619+
* @Route("/create", name="meetup_create")
620+
* @Template
621+
*/
622+
public function createAction(Request $request)
623+
624+
{
625+
$meetup = new SportMeetup();
626+
$form = $this->createForm(new SportMeetupType(), $meetup);
627+
$form->handleRequest($request);
628+
if ($form->isValid()) {
629+
// ... save the meetup, redirect etc.
630+
}
631+
632+
return array('form' => $form->createView());
633+
}
634+
// ...
635+
}
587636

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.
637+
The associated template uses some JavaScript to update the ``position`` form
638+
field according to the current selection in the ``sport`` field. To ease things
639+
it makes use of `jQuery`_ library and the `FOSJsRoutingBundle`_:
640+
641+
..code-block::html+jinja
642+
643+
{# src/Acme/DemoBundle/Resources/views/Meetup/create.html.twig #}
644+
{{ form_start(form) }}
645+
{{ form_row(form.sport) }} {# <select id="meetup_sport" ... #}
646+
{{ form_row(form.position) }} {# <select id="meetup_position" ... #}
647+
{# ... #}
648+
{{ form_end(form) }}
649+
650+
{# ... Include jQuery and scripts from FOSJsRoutingBundle ... #}
651+
<script>
652+
$(function(){
653+
// When sport gets selected ...
654+
$('#meetup_sport').change(function(){
655+
var $position = $('#meetup_position');
656+
// Remove current position options except first "empty_value" option
657+
$position.find('option:not(:first)').remove();
658+
var sportId = $(this).val();
659+
if (sportId) {
660+
// Issue AJAX call fetching positions for selected sport as JSON
661+
$.getJSON(
662+
// FOSJsRoutingBundle generates route including selected sport ID
663+
Routing.generate('meetup_positions_by_sport', {id: sportId}),
664+
function(positions) {
665+
// Append fetched positions associated with selected sport
666+
$.each(positions, function(key, position){
667+
$position.append(new Option(position[1], position[0]));
668+
});
669+
}
670+
);
671+
}
672+
});
673+
});
674+
</script>
675+
676+
The last piece is implementing a controller for the
677+
``meetup_positions_by_sport`` route returning the positions as JSON according
678+
to the currently selected sport. To ease things again the controller makes use
679+
of the:doc:`@ParamConverter</bundles/SensioFrameworkExtraBundle/annotations/converters>`
680+
listener to convert the submitted sport ID into a ``Sport`` object::
681+
682+
// src/Acme/DemoBundle/Controller/MeetupController.php
683+
// ...
684+
use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
685+
686+
/**
687+
* @Route("/meetup")
688+
*/
689+
class MeetupController extends Controller
690+
{
691+
// ...
692+
/**
693+
* @Route("/{id}/positions.json", name="meetup_positions_by_sport", options={"expose"=true})
694+
*/
695+
public function positionsBySportAction(Sport $sport)
696+
{
697+
$result = array();
698+
foreach ($sport->getAvailablePositions() as $position) {
699+
$result[] = array($position->getId(), $position->getName());
700+
}
701+
702+
return new JsonResponse($result);
703+
}
704+
}
705+
706+
..note::
707+
708+
The returned JSON should not be created from an associative array
709+
(``$result[$position->getId()] = $position->getName())``) as the iterating
710+
order in JavaScript is undefined and may vary in different browsers.
593711

594712
.. _cookbook-dynamic-form-modification-suppressing-form-validation:
595713

@@ -622,3 +740,6 @@ all of this, use a listener::
622740

623741
By doing this, you may accidentally disable something more than just form
624742
validation, since the ``POST_SUBMIT`` event may have other listeners.
743+
744+
.. _`jQuery`:http://jquery.com
745+
.. _`FOSJsRoutingBundle`:https://github.com/FriendsOfSymfony/FOSJsRoutingBundle

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp