0
votes

I'm quite new at Symfony, and having trouble with my form. I'm trying to fill a dropdown with the selected item of a fist one.

So far I was able to fill the first one with data, and have settled up an EventListener for the second one.

I'm stuck at getting it showned and filled from the choices made. I've followed the indications from the Symfony tutorial.

Here is my FormType :

public function buildForm(FormBuilderInterface $builder, array $options)
{       
    // Getting the Site(s) linked tu the User
    $sites = $this->controller->getSitesByUser();

    $builder
        ->add('site', EntityType::class,[
            'class' => Site::class,
            'data' => $sites,
            'label' => 'Site : ',
            'placeholder' => 'Choisissez un Site',
            'choice_label' => function($sites){
                return sprintf('%s',$sites->getNom());
            }
        ])
        ->add('appel', EntityType::class,[
            'class' => Appel::class
        ])
        ->add('startDate', DateType::class, [
            'label' => 'Date début :',
            'widget' => 'single_text',
            'attr' => ['class' => 'js-datepicker'],
            'label' => 'Date début :'
        ])
        ->add('endDate', DateType::class, [
            'label' => 'Date fin :',
            'widget' => 'single_text',
            'attr' => ['class' => 'js-datepicker'],
            'label' => 'Date fin :'
        ])
        ->add('generate', SubmitType::class, [
            'label' => 'Generer'
        ])
        ->addEventListener(FormEvents::PRE_SET_DATA, array($this, 'onPreSetData'))
        ->getForm();
}

function onPreSetData(FormEvent $event) {
    // GetData return a Fact Object
    $form = $event->getForm();

    // Get selected field from 'site'
    $entityObject = $form->get('site')->getData();
    // Get the id of the selected object
    if($entityObject != null){
        $selectedSite = $entityObject->getId();
    }else $selectedSite = null;

    //dd($selectedSite);
    //$appels = $this->controller->getPostesBySite($selectedSite);

    $this->addElements($form, $selectedSite);

}

private function addElements(FormInterface $form, $selectedSite){
    if (null === $selectedSite) {
        $form->remove('appel');
        return;
    }
    // Only if the user has selected a Site Entity
    if($selectedSite){
        $form->add('appel', EntityType::class,[
            'class' => Appel::class,
            'placeholder' => 'Sélectionnez un Poste',
            'label' => 'Poste : ',
            'multiple' => true,
            'required' => false,
            'query_builder' => function (AppelRepository $ar) use ($selectedSite) { // Variable to use in use (sic)
                return $ar->createQueryBuilder('u')
                    ->select('u')
                    ->where('u.site = :idSite')
                    ->orderBy('u.from_dispname', 'ASC')
                    ->setParameter('idSite', $selectedSite);
            },
        ]);
    }
    
}

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

and my Controller :

public function createfact(Request $request, $id, UserInterface $user = null) : Response {
    $fact = new Fact();
    $fact->setName('Facture n° '.$fact->getId(). 'par '.$fact->getUser());
    $fact->setDate(new \DateTime());

    $form = $this->createForm(FactFormType::class, $fact);

    $form->handleRequest($request);

    return $this->render('main/fact.html.twig', [
        'formFact' => $form->createView()
    ]);
}

and in my Twig :

{% block body %}
{{ form_start(formFact) }}
    {#{{ form_widget(formFact) }}#}
    {{ form_row(formFact.site)}}
    {%  if formFact.appel is defined %}
        {{ form_row(formFact.appel) }}
    {% endif %}
    {{ form_row(formFact.startDate)}}
    {{ form_row(formFact.endDate)}}
    {{ form_row(formFact.generate)}}
{{ form_end(formFact) }}

{% endblock %}

with this script inside the block body :

<script>
var $site = $('#fact_form_site');
// When sport gets selected ...
$site.change(function() {
// ... retrieve the corresponding form.
var $form = $(this).closest('form');
// Simulate form data, but only include the selected site value.
var data = {};
data[$site.attr('site')] = $site.val();
// Submit data via AJAX to the form's action path.
$.ajax({
    url : $form.attr('action'),
    type: $form.attr('method'),
    data : data,
    success: function(html) {
    // Replace current position field ...
    $('#fact_form_appel').replaceWith(
        // ... with the returned one from the AJAX response.
        $(html).find('#fact_form_appel')
    );
    // Position field now displays the appropriate positions.
    }
});
});

What am I missing ?

Thanks in advance.

2
if($entityObject != null){ $selectedSite = $entityObject->getId(); return;} what is the reason behind the return; here ? - zizoujab
No reason for it being here. Removed, thanks @zizoujab - Etik
Is the form submitted? Response status code is 200? (You can inspect the request content, status and form status in web profiler) - Alessandro Chitolina
Yes, upon loading the status in the profiler is 200 - Etik
Probably off-topic but you should not have getForm at then end of you buildForm method. - Cerad

2 Answers

0
votes

If found a workaroud for this, following Cerad's idea of using JS. As I said, it is a workaround and does not use the FormEvent.

First thing I did was spliting the two DropDown into two separate FormType.

Then, all the handling is done in the Twig:

{% block body %}

{{ form_start(formFact) }}
    {#{{ form_widget(formFact) }}#}
    {{ form_widget(formFact) }}
    {{ form_widget(formFact2) }}
    <button type="submit" class="btn btn-primary">Générer la Facture</button>
{{ form_end(formFact) }}

<script>
    $(document).ready(function(){ 
        $("#fact_form_site").change(function() {
            var vendor = $('#fact_form_site option:selected').val()
            var DATA = 'id=' + vendor;
            
            $.ajax({
                type: "POST",
                dataType: 'json',
                url:  '{{ (path('appelfact')) }}',
                data: DATA,
                success: function(msg){
                    $("#fact_form2_appel")                                                              // Getting the DropDown
                        .find('option')                                                                 // Finding <option>
                        .remove()                                                                       // Clearing the options from DropDown
                        .end()                                                                          // Commit changes
                        .append('<option value="" selected="selected">Sélectionnez un Poste</option>'), // Getting message back as first value
                    $.each(msg, function (i, value) {
                        var html='<option value="'+i +'">' + value + '</option>'
                        $("#fact_form2_appel").append(html);
                    });
                }
            });
        }); 
    });
</script>

{% endblock %}

And my slightly modified Controller:

/**
 * @Route("main/fact/{id}", defaults={"id" = null}, name="fact")
 */
public function createfact(Request $request, $id) : Response {
    $form = $this->createForm(FactFormType::class);
    $form2 = $this->createForm(FactForm2Type::class);


    $form->handleRequest($request);
    if ($form->isSubmitted() && $form->isValid()) {
        dd($form->getData());
    }

    return $this->render('main/fact.html.twig', [
        'formFact' => $form->createView(),
        'formFact2' => $form2->createView()
    ]);
}

This works, and I've added some lines to clear the Dropdown between selects. If it could help someone, this is a much simpler task than using FormEvent.

-1
votes

Your controller doesn 't send the form. You forgot to do if ($form->submit && $form->isValid) and your condition