web-dev-qa-db-fra.com

soumettre le formulaire symfony 3 avec ajax

J'essaie d'implémenter mes formulaires symfony/modal, avec ajax pour arrêter le rechargement de la page chaque fois que je soumets une action d'ajout/suppression et de mise à jour, mais le problème que je ne connais pas avec ajax et je ne sais pas comment fais le. Quelqu'un peut-il m'aider à comprendre le concept.

mon entité:

<?php

namespace EvalBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;


/**
 * Department
 *
 * @ORM\Table(name="department")
 * @ORM\Entity(repositoryClass="EvalBundle\Repository\DepartmentRepository")
 */
class Department
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="name", type="string",unique=true)
     */
    private $name;


    /**
     * One Department has Many Collaborators.
     * @ORM\OneToMany(targetEntity="Collaborator", mappedBy="department")
     */
    private $collaborators;


    /**
     * Get id
     *
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set name
     *
     * @param string $name
     *
     * @return Department
     */
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

    /**
     * Get name
     *
     * @return string
     */
    public function getName()
    {
        return $this->name;
    }
}

forme :

<?php

namespace EvalBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class DepartmentType extends AbstractType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('name');
    }

    /**
     * {@inheritdoc}
     */
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'EvalBundle\Entity\Department',
            'attr' => array('novalidate' => 'novalidate')

        ));
    }

    /**
     * {@inheritdoc}
     */
    public function getBlockPrefix()
    {
        return 'evalbundle_department';
    }


}

Manette :

<?php
/**
 * Created by PhpStorm.
 * User: sa7noun
 * Date: 5/15/17
 * Time: 12:09 PM
 */

namespace EvalBundle\Controller;

use EvalBundle\Entity\Department;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Method;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Component\HttpFoundation\Request;

class DepartmentController extends Controller
{

    /**
     * Lists all Department entities.
     *
     * @Route("/department", name="department_index")
     * @Method({"GET","POST"} )
     *
     */
    public function indexAction(Request $request)
    {

        $department = new Department();
        $form = $this->createForm('EvalBundle\Form\DepartmentType', $department);
        $form->handleRequest($request);


        if ($form->isSubmitted() && $form->isValid()) {

            $em = $this->getDoctrine()->getManager();
            $em->persist($department);
            $em->flush();
            return $this->redirectToRoute('department_index');
        }

        $em = $this->getDoctrine()->getManager();
        $departments = $em->getRepository('EvalBundle:Department')->findAll();
        /**
         * @var $paginator \Knp\Component\Pager\Paginator
         */
        $paginator = $this->get('knp_paginator');
        $result = $paginator->paginate(
            $departments,
            $request->query->getInt('page', 1),
            $request->query->getInt('limit', 5)
        );

        return $this->render('EvalBundle:Department:department.html.twig', array(
            'departments' => $result,
            'form' => $form->createView(),
        ));

    }

//    /**
//     * Creates a new Department entity.
//     *
//     * @Route("/department/new", name="department_new")
//     * @Method({ "POST"})
//     */
//    public function newAction(Request $request)
//    {
//        $department = new Department();
//        $form = $this->createForm('EvalBundle\Form\DepartmentType', $department);
//        $form->handleRequest($request);
//
//        if ($form->isSubmitted() && $form->isValid()) {
//            $em = $this->getDoctrine()->getManager();
//            $em->persist($department);
//            $em->flush();
//
//            return $this->redirectToRoute('department_index');
//        }
//
//        return $this->render('EvalBundle:Department:department.html.twig', array(
//            'department' => $department,
//            'form' => $form->createView(),
//        ));
//    }


    /**
     * Displays a form to edit an existing department entity.
     *
     * @Route("department/{id}/edit", name="department_edit")
     * @Method({"GET", "POST"})
     */
    public function editAction(Request $request, Department $department)
    {
        $deleteForm = $this->createDeleteForm($department);
        $editForm = $this->createForm('EvalBundle\Form\DepartmentType', $department);
        $editForm->handleRequest($request);


        if ($editForm->isSubmitted() && $editForm->isValid()) {
            $this->getDoctrine()->getManager()->flush();

            return $this->redirectToRoute('department_edit', array('id' => $department->getId()));
        }

        return $this->render('EvalBundle:Department:edit.html.twig', array(
            'department' => $department,
            'edit_form' => $editForm->createView(),
            'delete_form' => $deleteForm->createView(),
        ));
    }

    /**
     * Deletes a department entity.
     *
     * @Route("department/{id}", name="department_delete")
     * @Method({"GET","DELETE"})
     */
    public function deleteAction(Department $department)
    {

//        $response = array(
//            'success' => true,
//            'message' => '',
//            'html' => '',
//        );
//
//          $form = $this->createDeleteForm($department);
//        if ($request->getMethod() == 'DELETE'){
//            $form->handleRequest($request);
//        }
//
        if ($department) {
            $em = $this->getDoctrine()->getManager();
            $em->remove($department);
            $em->flush();
        }

        return $this->redirectToRoute('department_index');
    }

    /**
     * Creates a form to delete a department entity.
     *
     * @param Department $department The department entity
     *
     * @return \Symfony\Component\Form\Form The form
     */
    private function createDeleteForm(Department $department)
    {
        return $this->createFormBuilder()
            ->setAction($this->generateUrl('department_delete', array('id' => $department->getId())))
            ->setMethod('DELETE')
            ->getForm();
    }

} 

Vue (Index):

    {% extends 'default/superAdminBase.html.twig' %}
    {% block body %}

        <div class="col-lg-6">
            <div class="panel panel-default">
                <div class="panel-heading" style="background-color: #0089db">
                    <h5 style="text-align: center"><b>Départements</b></h5>
                </div>
                <!-- /.panel-heading -->
                <div class="panel-body">
                    <div class="table-responsive">
                        <table class="table table-hover table-fixed table-paginated">
                            <thead>
                            <tr>
                            </tr>
                            </thead>
                            <tbody>
                            {% for department in departments %}
                                <tr>
                                    <td>
                                        <b>{{ department.name }}</b>
                                        <a href="{{ path('department_edit', { 'id': department.id }) }}"
                                           class="btn btn-default btn-circle " style="float: right">
                                            <i class="fa fa-edit"></i>
                                        </a>
                                        <a href="{{ path('department_delete', {'id': department.id}) }}"
                                           class="btn btn-danger btn-circle remove-item"
                                           data-entity-id="{{ department.id }}" style="float: right" data-toggle="modal">
                                            <span class="glyphicon glyphicon-remove"></span>
                                        </a>
                                        <div class="modal fade" id="infos">
                                            <div class="modal-dialog">
                                                <div class="modal-content">
                                                    <div class="modal-header">
                                                        <button type="button" class="close" data-dismiss="modal">x</button>
                                                        <h4 class="modal-title">Confirmation</h4>
                                                    </div>
                                                    <div class="modal-body">
                                                        Etes-vous sur de vouloir supprimer ce Département !
                                                    </div>
                                                    <div class="modal-footer">
                                                        <button href=" #" class="btn btn-info delete-item"
                                                                data-dismiss="modal">OUI
                                                        </button>
                                                        <button class="btn btn-info" data-dismiss="modal">NON</button>
                                                    </div>
                                                </div>
                                            </div>
                                        </div>
                                    </td>
                                </tr>
                            {% endfor %}
                            </tbody>
                        </table>
                    </div>
                    <!-- /.table-responsive -->
                </div>
                <!-- /.panel-body -->
            </div>
            <div class="navigation text-center">
                {{ knp_pagination_render(departments) }}
            </div>

            <!-- /.panel -->
            <div aria-hidden="true" aria-labelledby="myModalLabel" role="dialog" tabindex="-1" id="myModal-1" class="modal fade">
                <div class="modal-dialog">
                    <div class="modal-content">
                        <div class="modal-header">
                            {% if app.session.flashBag.has('success') %}
                                <div class="aler alert-success">
                                    {% for msg in app.session.flashBag.get('success') %}
                                        {{ msg }}
                                    {% endfor %}
                                </div>
                            {% endif %}

                            <button aria-hidden="true" data-dismiss="modal" class="close" type="button">×</button>
                            <h4 class="modal-title"> Ajouter un nouveau département</h4>
                        </div>
                        <div class="modal-body" id="modal-input">
                            {{ form_start(form,{'attr': {'class': 'form-horizontal','data-parsley-validate':''}}) }}
                            {{ form_widget(form.name,{'attr': {'class': 'form-control','placeholder':'Nom de département', 'data-parsley-required':'true', 'data-parsley-required-message':'le nom ne doit pas être vide :D'}}) }}
                            <br>

                            <div class="form-group">
                                <div class="col-lg-offset-8 col-lg-4">
                                    <button type="submit" class="btn btn-block btn-primary"><span
                                                class="glyphicon glyphicon-plus"></span> Créer
                                    </button>
                                </div>
                            </div>
                            {{ form_end(form) }}
                        </div>
                    </div>
                </div>
            </div>
        </div>
        <a href="#myModal-1" data-toggle="modal" class="btn btn-outline btn-primary "><i class="fa fa-plus"></i>Ajouter un
            département</a>
        {% block javascript %}
            <script src="{{ asset('JS/departmentValidation.js') }}"></script>
        {% endblock %}
    {% endblo

ck %}
11
sahnoun

je vais répondre à cela très fondamentalement pour vous permettre de vous faire une idée!

vous devrez donc tout d'abord séparer la partie d'enregistrement côté serveur, car elle ne renverra plus de vue comme le fait votre indexAction. Au lieu de cela, il renvoie des données json que votre appel ajax du côté client peut recevoir

votre nouvelle action de contrôleur peut ressembler à ceci:

   /**
    * Creates a new Department entity.
    *
    * @Route("/department/new", name="department_new")
    * @Method({ "POST"})
    */
   public function newDepartmentAction(Request $request)
   {
        $department = new Department();
        $form = $this->createForm('EvalBundle\Form\DepartmentType', $department);
        $form->handleRequest($request);
        $status = "error";
        $message = "";

        if ($form->isSubmitted() && $form->isValid()) {
            $em = $this->getDoctrine()->getManager();
            $em->persist($department);
            try {
                $em->flush();
                $status = "success";
                $message = "new department saved";
            } catch (\Exception $e) {
                    $message = $e->getMessage();
            }    
        }else{
            $message = "invalid form data";
        }

        $response = array(
            'status' => $status,
            'message' => $message
        );

        return new JsonResponse($response);

        // above is just an example of one way using formtypes, 
        // you can retrieve any parameter you send here like: 
        // $param = $request->get('param');


   }

vous pouvez faire ci-dessus ce que vous voulez comme paginer sur tous les départements et les renvoyer, mais vous auriez besoin d'un moyen js pour afficher le JSON retourné, vous ne pouvez pas utiliser twig pour cela parce que la vue est déjà retournée, vous voulez certainement pour utiliser n'importe quelle bibliothèque de modèles de données JS View avec actualisation automatique de l'interface utilisateur.

Ensuite, le côté client - Du côté client, vous devrez envoyer les données correctes à cette action

vous devez donc sérialiser les champs de formulaire en un ensemble de propriétés et de valeurs que vous pouvez envoyer au serveur. Nous allons d'abord sérialiser le formulaire en un objet javascript.

ici, vous avez une fonction pour cela que vous devez inclure quelque part après le chargement de jquery et avant votre code supplémentaire

$.fn.serializeObject = function()
{
    var o = {};
    var a = this.serializeArray();
    $.each(a, function() {
        if (o[this.name] !== undefined) {
            if (!o[this.name].Push) {
                o[this.name] = [o[this.name]];
            }
            o[this.name].Push(this.value || '');
        } else {
            o[this.name] = this.value || '';
        }
    });
    return o;
};

Ensuite, vous devez éviter de soumettre le formulaire non ajax, car en cliquant sur le bouton Soumettre, vous soumettez le formulaire et rechargez la page, nous empêchons ce comportement, en supposant que le formulaire possède un sélecteur eG unique. id = "newDepartmentForm"

$(document).on("submit", "#newDepartmentForm", function(e){
    e.preventDefault();
    return  false;
});

permet maintenant de supposer que vous souhaitez enregistrer en cliquant sur un bouton avec un identifiant spécifique

$(document).on("click", "#mySubmitButton", function(e){
  e.preventDefault();
  var form = $("#newDepartmentForm");

  // you could make use of html5 form validation here
  if(!form[0].checkValidity()){

    // To show the native error hints you can fake a click() on the actual submit button
    // which must not be the button #mySubmitButton and shall be hidden with display:none;
    //  example:
    //  <button type="button" id="#mySubmitButton" class"btn btn-default" > Save </button>
    //  <button type="submit" id="#myHIDDENSubmitButton" style="display:none;"></button>
    //
    $("#myHIDDENSubmitButton").click();
    return false;
  }

  // get the serialized properties and values of the form 
  var form_data = form.serializeObject();

  // always makes sense to signal user that something is happening
  $('#loadingSpinner').show();

  // simple approach avoid submitting multiple times
  $('#mySubmitButton').attr("disabled",true);

  // the actual request to your newAction
  $.ajax({
    url: '/department/new',
    type: 'POST',
    dataType: 'json',
    data: form_data,
    success:function(data){

      // handling the response data from the controller
      if(data.status == 'error'){
        console.log("[API] ERROR: "+data.message);
      }
      if(data.status == 'success'){
        console.log("[API] SUCCESS: "+data.message);
      }

      // signal to user the action is done
      $('#loadingSpinner').hide();
      $('#mySubmitButton').attr("disabled",false);
    }
  });
});

c'est essentiellement ça.

si vous voulez que votre site soit entièrement axé sur Ajax, vous pouvez demander toutes les données au serveur comme ceci, par exemple, vous pouvez d'abord charger tous les départements existants, vous pouvez simplement le faire comme ci-dessus. Mais comme je l'ai mentionné, vous auriez besoin d'un moyen JS pour afficher vos données, des termes comme une application d'une seule page, MVVM pourrait valoir le coup d'œil, il existe de nombreuses bibliothèques utiles comme vue, react, knockout, ember ... etc. si vous préférez un moyen simple, ils peuvent ne pas être nécessaires selon la complexité de votre modèle. Pour votre Api, vous pouvez également creuser davantage dans la sérialisation performante, REST, CRUD, autorisation et ne vous répétez pas. Les Websockets peuvent également être très intéressantes.

23
john Smith