原文地址:http://blog.csdn.net/nuaazdh/article/details/37660377,转载请勿标明,多谢~
前言
Symfony2.x本来应该许配给Bootstrap的,可是考虑到本人前端基础实在太差,使用Bootstrap还要使用DIV来布局,前端部分又得使用jQuery,因此就委屈求全使用了EasyUI。EasyUI界面不够炫,但确实方便。不过,之前没有用过,开始做还是遇到不少问题。闲话少叙,文章的主要内容是Symfony2.x作为框架,Doctrine从后台获取数据,并以json格式返回给前端。前端使用EasyUI的界面。其中重点是解决datagrid的异步加载的分页问题,如果没有分页需求可参见另一篇博文:Symfony2.x + EasyUI datagrid Ajax方式实现数据交互。
正文
一、分页问题的出现
首先明确一下为何会有分页问题。前一篇博文中给出的解决方案虽然是ajax异步加载,但是采用的是自动分页,即数据从服务端传来,直接赋值给datagrid控件。如果你的datagrid控件设置了pagination = true,它会自动加载一个分页条,实现自动分页。既然都自动分页了,那还有讨论这个问题干嘛? 假设你有这样的需求,后台的数据量很大,即使使用ajax数据量也太多,这样前端其实响应还是比较慢的,我们需要后端分页后每次只传给一页的数据量。其次,如果你的数据太多,它自动分页了,你要添加“编辑”、“删除”的按钮,编辑后需要更新显示,那么前面的做法就不行了,只能刷新整个页面,但是刷新后会自动显示第一页,不是你当时编辑的页面,怎么办?
二、 分页问题解决方案
解决方案: 将datagrid 控件和 分页pagination控件分开,datagrid 不再设置 pagination = true, 手动实现点击下一页的js代码。当用户点击”下一页“时,发出ajax请求,服务端根据传入的page (页的序号)和 limits(每页的记录个数)查询数据库,返回json结果。前端将返回的结果绑定到datagrid控件中。具体有以下几点:
① 前端(模板)中将 datagrid 和 pagination 控件分开,设置 手动完成切换页面的触发onSelectPage()函数;
② 在 onSelectPage()函数中,发出ajax请求,将页号(page)和每页记录数(limits或者rows)发送给服务端;
③ 服务端根据page和limits参数,做查询操作,返回从 (page-1)*limits 开始的limits个记录;
④ 将后端返回的数据绑定到前端的datagrid中,异步刷新;
⑤ 当前端发生单个记录的编辑操作时,编辑完成后,调用$('#pp').pagination('select'); (其中pp就是pagination控件的id),自动刷新当前页;
三、代码
路由文件和布局模板直接省略,直接看功能页面的twig文件和后端的获取分页数据的代码:
1. 前端twig模板代码
{% extends 'AcmeStoreBundle::layout.html.twig' %}
{% block content %}
<!--这里是你的datagrid控件,注意不要设置 pagination = true 了 -->
<table id="dg" title="Title name" class="easyui-datagrid" style="width:auto;height:0"
toolbar="#toolbar" rownumbers="true" fitColumns="true" singleSelect="true">
<thead>
<tr>
<th data-options="field:'id',width:0,align:'left'">ID</th>
<th data-options="field:'name',width:60,align:'left'">名称</th>
</tr>
</thead>
</table>
<!-- 这里是你的pagination 控件,用于分页 -->
<div id="pp" class="easyui-pagination" style="border:1px solid #ccc;"
data-options="
total: 200,
pageList: [10,20,50,100],
onSelectPage: function(pageNumber, pageSize){ // 页面切换动作
getDataByPageRows(pageNumber,pageSize);
}">
</div>
<!-- 这里省略了form表单的定义 -->
<script>
$(function(){ // 页面初始加载的时候,显示第一页,10条记录
getDataByPageRows(1,10);
});
function saveItem(){ // 前端form发生编辑保存动作
// 表单数据转化为 json格式的字符串
var raw_str = JSON.stringify($('#fm').serializeArray());
$('#fm').form('submit',{
onSubmit: function(){
$.ajax({
type: "POST",
dataType: 'json', // 格式为json
url: "{{ path('xxx_update') }}",// 执行动作的路由名称,在routing.yml中定义
data: {
data_p: raw_str,
},
error: function(){
alert('对不起,保存数据失败!');
$('#dlg').dialog('close');
},
success: function(data,textStatus,jqXHR){
$('#dlg').dialog('close');
$.messager.alert('提示信息:',data,'info');
$('#pp').pagination('select');
}
});//ajax
}
});
}
/*
* ajax 方式向后端请求第pageNum页,共rowLimit条记录
*/
function getDataByPageRows(pageNum, rowsLimit)
{
pageNum = pageNum || 1; // 设置默认的页号
rowsLimit = rowsLimit || 10;// 设置默认的每页记录数
$.ajax({
type: "POST",
dataType: 'html', // 注意格式是html,不是json
url: "{{ path('xxx_show') }}",
data: {
page: pageNum,
rows: rowsLimit,
},
error: function(){ // ajax请求失败
$.messager.show({
title:'失败信息',
msg:'load data failed',
timeout:0,
showType:'fade'
});
},
success: function(data,textStatus,jqXHR){ // 请求成功,将返回的数据(一页的记录数)绑定到 datagrid控件
var jsonObj = $.parseJSON(data);
var count = jsonObj.count; // 总记录个数
var data_t = eval('['+jsonObj.data+']');
$('#dg').datagrid('loadData', data_t);
$('#pp').pagination({
total: count, // 由于显示 ”共XXX条记录” 等信息用
pageNumber: pageNum, //
});
}
});//ajax
}
</script>
<!-- 这里可以添加多个 toolbar -->
<div id="toolbar">
<div style="margin-bottom:5px">
<a href="javascript:void(0)" class="easyui-linkbutton" iconCls="icon-add" plain="true" οnclick="newItem()">新建</a>
<a href="javascript:void(0)" class="easyui-linkbutton" iconCls="icon-edit" plain="true" οnclick="editItem()">编辑</a>
<a href="javascript:void(0)" class="easyui-linkbutton" iconCls="icon-remove" plain="true" οnclick="deleteItem()">删除</a>
</div>
</div>
{% endblock %}
2. 后端更新操作和显示一页的php代码
<?php
// src/Acme/StoreBundle/Controller/XXXController.php
namespace Acme\StoreBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Doctrine\ORM\Tools\Pagination\Paginator;
use Acme\StoreBundle\Entity\XXXEntity;
class XXXController extends Controller{
/**
*@Route("/XXX/",name="XXX_index")
*@Template("AcmeStoreBundle:XXXEntity:index.html.twig")
*/
public function showAction(Request $request)
{
// 仅处理ajax响应
if($request->isXmlHttpRequest()){
// 获取请求参数
$rows = $request->request->get('rows');
$page = $request->request->get('page');
// 设置请求参数的默认值,若前端js中处理了,此处可以省略
isset($rows)?($rows):($rows=10);
isset($page)?($page):($page=1);
// 这是是使用Doctrine获取分页的关键代码
$em = $this->getDoctrine()->getManager();
$dql = 'SELECT p FROM AcmeStoreBundle:XXXEntity p ORDER BY p.id ASC';
$query = $em->createQuery($dql)
->setFirstResult(($page-1)*$rows)
->setMaxResults($rows);
$paginator = new Paginator($query,true);
// json 字符串
$json_str = "";
$i = 0;
$len = count($paginator);
foreach ($paginator as $item)
{
$i++;
// 这里提供日期和时间类型转换函数,供需要的参考
//$publishDateStr = date_format($item->getPublishDate(), 'Y-m-d');
//$signitureTimeStr = date_format($item->getSignitureTime(), 'Y-m-d H:i:s');
$publishTypeStr = addslashes($item->getPublishType());
$json_str.="{id:"."\"{$item->getId()}\","
."name:"."\"{$item->getName()}\",";
if($i!=$len){// 对最后一个记录做特殊处理:省略右花括号}后的逗号,
$json_str .= "lastColumnName:"."\"{$cutsizeStr}\"},";
}else{
$json_str .= "lastColumnName:"."\"{$cutsizeStr}\"}";
}
}
// 返回记录的总数,和当前页的所有记录数据,记录总数会在分页控件中用到,"共xxx项,当前显示xxx-xxx项"
return new Response(json_encode(array('count' => $len,'data' => $json_str)));
}else{
return new Response('Illigal request');
}
}
/**
*@Route("/XXX/",name="XXX_index")
*@Template("AcmeStoreBundle:XXXEntity:index.html.twig")
*/
public function updateAction(Request $request)
{
if ($request->isXmlHttpRequest()) {
$json_data = $request->request->get('data_p');
$data = json_decode($json_data); // 解析 json数据
// 获取各个属性值
$id = (int)$data[0]->{"value"};
$name = $data[1]->{"value"};
// 提供两个日期和时间的处理函数,供需要参考
//$publishDate = date_create_from_format('Y/m/j',$data[2]->{"value"});
//$signitureTime = date_create_from_format('Y/m/j H:i:s',$data[3]->{"value"});
// 在 Repository中根据id查询记录
$em = $this->getDoctrine()->getEntityManager();
$item = $em->getRepository('AcmeStoreBundle:XXXEntity')->find($id);
if(!$item){ // 记录不存在,可以创建,这样一个updateAction完成了前端“新建”和“编辑”两个功能
$item = new XXXEntity();
$item->setName($name);
//$item->setpublishDate($publishDate);
//$item->setSignitureTime($signitureTime);
$em->persist($item);
$em->flush();
return new Response(json_encode('创建项目成功,ID:'.$item->getId()));
}else{ // 记录存在,更新值
$item->setName($name);
//$item->setpublishDate($publishDate);
//$item->setSignitureTime($signitureTime);
$em->flush();
// 注意返回的数据格式也是json_encode()处理过的,否则前端ajax获取失败
return new Response(json_encode('更新 ID = '.$id.' 文件信息成功!'));
return $response;
}
}else{
return new Response(json_encode('非法的更新请求!'),400);
}
}
}
?>
3. 页面效果
Firefox的Firebug大家都比较熟悉,不用推荐了,贴一下效果,easyui的界面都差不多,呵