【问题标题】:submit symfony 3 form with ajax使用 ajax 提交 symfony 3 表单
【发布时间】:2017-10-20 15:08:01
【问题描述】:

我正在尝试实现我的 symfony 表单/模式,每次提交添加/删除和更新操作时,我都会使用 ajax 停止重新加载页面,但是我不熟悉 ajax 并且我不熟悉的问题知道该怎么做。谁能帮我理解这个概念。

我的实体:

<?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;
    }
}

形式:

<?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';
    }


}

控制器:

<?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();
    }

} 

查看(索引):

    {% 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 %}

【问题讨论】:

    标签: jquery ajax forms symfony modal-dialog


    【解决方案1】:

    我会非常基本地回答这个问题,让你有个想法!

    所以首先你必须在服务器端分离保存部分,因为它不会像你的 indexAction 那样返回一个视图。相反,它会返回一些您在客户端的 ajax 调用可以接收的 json 数据

    您的新控制器操作可能如下所示:

       /**
        * 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');
    
    
       }
    

    你可以在上面做任何你想做的事情,比如对所有部门进行分页并返回它们,但是你需要一种 js 方式来显示返回的 JSON,你不能使用 twig 因为视图已经返回,你肯定想要使用具有自动 ui 刷新功能的任何数据驱动的 JS 视图模型库。

    接下来,客户端 - 您必须从客户端向该操作发送正确的数据

    因此您必须将表单域序列化为一组可以发送到服务器的属性和值。我们将首先将表单序列化为 javascript 对象。

    这里你有一个函数,你必须在 jquery 加载之后和你的进一步代码之前的某个地方包含它

    $.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;
    };
    

    接下来您必须避免实际提交非 ajax 表单,因为单击提交按钮会导致提交表单并重新加载页面,我们会阻止这种行为,假设表单具有一些独特的选择器 eG。 id="newDepartmentForm"

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

    现在假设您要通过单击具有特定 ID 的按钮来保存

    $(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);
        }
      });
    });
    

    基本上就是这样。

    如果你想让你的网站完全由 Ajax 驱动,你可以像这样从服务器请求任何数据,例如你可能想先加载所有现有的部门,你可以像上面那样做。 但正如我所提到的,你需要一种 JS 方式来显示你的数据,像单页应用程序这样的术语,MVVM 可能值得一看,有很多有用的库,比如 vue,react,knockout,ember 等等。如果你更喜欢简单的方法,根据模型的复杂性,它们可能不是必需的。 对于您的 Api,您还可以深入挖掘性能序列化、REST、CRUD、授权,不要重复自己。 Websockets 也可能很有趣。

    【讨论】:

    • 感谢 john 的重播,我想问你这是否适用于我创建的引导模式?
    • 当然,绝对没有理由不这样做。但进一步调查是你的一部分
    • 这是一个很好的起点,对我帮助很大。但是,如果您在表单中有很好的输入,这将不起作用。我建议使用jQuery form plugin,它甚至会序列化表单输入并简化 ajax 表单提交。如果你想要一个本地方法,在现代浏览器中你可以使用FormData 序列化数据表单输入和文件
    • 确实如此,感谢您指出这一点。我的示例不适用于文件,但只想提一下,在我的示例中实现 FormData 并不困难,ajax-call-properties 的数据属性只需要像data = new FormData(); 这样的formdata 并使用data.append('key','value') 添加参数
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-01-14
    • 1970-01-01
    • 2018-10-30
    • 1970-01-01
    • 2017-02-03
    • 1970-01-01
    • 2017-05-20
    相关资源
    最近更新 更多