使用 ajax 提交 symfony 3 表单
我正在尝试实现我的 symfony 表单/模式,每次提交添加/删除和更新操作时,我都会使用 ajax 停止重新加载页面,但是我不熟悉 ajax 并且我不熟悉的问题知道该怎么做.谁能帮我理解这个概念.
I'm trying to implement my symfony forms / modal ,with ajax to stop reloading page every time time I submit an add/remove and update action, but the problem that I'm not familiar with ajax and I don't know how to do it. Can anyone help me understand the concept.
我的实体:
<?php
namespace EvalBundleEntity;
use DoctrineORMMapping as ORM;
use SymfonyComponentValidatorConstraints as Assert;
/**
* Department
*
* @ORMTable(name="department")
* @ORMEntity(repositoryClass="EvalBundleRepositoryDepartmentRepository")
*/
class Department
{
/**
* @var int
*
* @ORMColumn(name="id", type="integer")
* @ORMId
* @ORMGeneratedValue(strategy="AUTO")
*/
private $id;
/**
* @var string
*
* @ORMColumn(name="name", type="string",unique=true)
*/
private $name;
/**
* One Department has Many Collaborators.
* @ORMOneToMany(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 EvalBundleForm;
use SymfonyComponentFormAbstractType;
use SymfonyComponentFormFormBuilderInterface;
use SymfonyComponentOptionsResolverOptionsResolver;
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' => 'EvalBundleEntityDepartment',
'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 EvalBundleController;
use EvalBundleEntityDepartment;
use SymfonyBundleFrameworkBundleControllerController;
use SensioBundleFrameworkExtraBundleConfigurationMethod;
use SensioBundleFrameworkExtraBundleConfigurationRoute;
use SymfonyComponentHttpFoundationRequest;
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('EvalBundleFormDepartmentType', $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 KnpComponentPagerPaginator
*/
$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('EvalBundleFormDepartmentType', $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('EvalBundleFormDepartmentType', $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 SymfonyComponentFormForm 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 %}
推荐答案
我会很基本的回答这个问题,让你有个想法!
i will answer this very basically to let you get an idea !
所以首先你必须在服务器端分离保存部分,因为它不会像你的 indexAction 那样返回一个视图.相反,它会返回一些您在客户端的 ajax 调用可以接收的 json 数据
so first of all you will have to separate the saving part on the server side, because it will not return a view anymore like your indexAction does. Instead it returns some json data your ajax call on the client side can receive
您的新控制器操作可能如下所示:
your new controller action may look somelike this:
/**
* Creates a new Department entity.
*
* @Route("/department/new", name="department_new")
* @Method({ "POST"})
*/
public function newDepartmentAction(Request $request)
{
$department = new Department();
$form = $this->createForm('EvalBundleFormDepartmentType', $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 视图模型库.
you can do above whatever you want like paginate over all departments and return them, but you would need a js way to display the returned JSON then, you cant use twig for that because the view is already returned, you definetly want to use any datadriven JS View Model lib with automatic ui refresh.
接下来,客户端 - 您必须从客户端向该操作发送正确的数据
Next, The client Side - From the client side you will have to send the the correct data to that action
因此您必须将表单域序列化为一组可以发送到服务器的属性和值.我们将首先将表单序列化为 javascript 对象.
so you have to serialize the formfields to a set of properties and values that you can send to the server. We will first serialize the form to a javascript object.
这里你有一个函数,你必须在 jquery 加载之后和你的进一步代码之前的某个地方包含它
here you have a function for that that you have to include somewhere after jquery has loaded and before your further code
$.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"
Next you have to avoid actually submitting the form non-ajax, because clicking on the submit button will lead to submit the form and reload the page, we prevent that behaviour, assuming the form has some unique selector eG. id="newDepartmentForm"
$(document).on("submit", "#newDepartmentForm", function(e){
e.preventDefault();
return false;
});
现在假设您要通过单击具有特定 ID 的按钮来保存
now lets assume you want to save by clicking on a button with specific 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 也可能很有趣.
if you want to make your site full Ajax-driven, you can request any data from the server like this, for example you may want to load all existing departments first you could just do it like above. But as i mentioned, you would need a JS way to Display your data, terms like single page application, MVVM could be worth a lookup there are a lot of usefull libraries like vue, react, knockout, ember ... etc. if you prefer an easy way, they may not be neccessary depending on the complexity of your model. For your Api you also may dig more into performant serialization, REST, CRUD, authorization and dont repeat yourself. Websockets may also be very interesting.
相关文章