前言
Ajax+PHP实现动态无刷新技术应用及其广泛,因为它有着可动态的实现WEB页面局部刷新,减轻服务器端负担,按需取数据,能带来更好的用户体验的优势。主要适用的场景像一些表单驱动的交互,普通的文本输入提示和自动完成,投票yes/no等无关痛痒的场景等等。根据文章的标题,大家应该看出来了,我在“部门和人员”这一模块当中就用了无刷新的技术,一并结合Yii2自带的Csrf验证。在这么使用的时候,我遇到了一些问题,也想出了一些解决方式,今天花了点时间整理一下,分享了。
需求分析
1、做一个部门和人员模块全部使用Aajx无刷新技术开发。
2、要求使用Yii2自带的Csrf验证。
效果图
部门和人员模块效果
Csrf验证
代码分析
结合需求,在点击的按钮上全部加上onClick点击事件,然后function事件的方法通过Ajax把数据传到控制器处理,返回进行局部刷新页面,查看结果是否正确,实现思路大致就这样。这么说可能还有人迷糊,下面我附上代码,因代码量太大,所以我只附关键部分。
以添加员工为例:
1、视图部分
点击添加员工按钮,在添加员工按钮上定义个onClick="AddUser()"事件,因为我用的是a标签,所以为了避免双重点击跳转,这里我把a标签里的href设为“javascript:void(0);”禁止跳转。
添加员工
点击提交表单按钮,通过Ajax提交。注意这里我使用的是Button,而不是submitButton,使用submitButton无法实现无刷新。
'btn btn-primary','onClick'=>"UUpdate1()"]) ?>
2、JS部分。
我分成两步走,第一步是点击添加展示视图页面,第二部分是添加部门提交表单验证,返回局部刷新页面。
第一步:
//列表添加员工
function AddUser(){
$.ajax({
type : "post",
url : "/department/tree",
dataType : "text",
data : {'AddUser':'AddUser'}, //固定参数,区分用
success : function(data){
$('#href_data').html(data);
}
});
}
第二步:
//列表添加员工提交表单验证
function AddUser1(){
var id=$("#current_dep").val();
var company_id=$("#current_com").val();
var parent_id=$('#user-parent_id').val();
var t_nickname=$('#user-t_nickname').val();
var t_realname=$('#user-t_realname').val();
var t_mobile=$('#user-t_mobile').val();
var t_email=$('#user-t_email').val();
//获取选中部门
var arr = document.getElementsByName('department_ids[]');
var arrLen = arr.length;
var chk = [];
var chkPoint = 0;
for (var i = 0; i < arrLen; i ++) {
if(arr[i].checked == true){
chk[chkPoint] = arr[i].value;
chkPoint ++;
}
}
//获取选中职位
var posit = document.getElementsByName("UserProfile[position][]");
var arrlen = posit.length;
var position = [];
var chkpoint = 0;
for (var i = 0; i < arrlen; i ++) {
if(posit[i].checked == true){
position[chkpoint] = posit[i].value;
chkpoint ++;
}
}
//昵称,真实姓名,邮箱同时存在才能提交表单
if(t_nickname && t_realname && t_email){
$.ajax({
type : "post",
url : "/department/tree",
dataType : "text",
data : {
'UId':id,
'UCompany_id':company_id,
'SEdit':'SEdit',
'add_user':'add_user',
'User[parent_id]':parent_id,
'User[t_nickname]':t_nickname,
'User[t_realname]':t_realname,
'User[t_mobile]':t_mobile,
'User[t_email]':t_email,
'department_ids':chk,
'UserProfile[position]':position,
'_csrf':'request->getCsrfToken (); ?>'
},
success : function(data){
$('#href_data').html(data);
}
});
}else{
$.ajax({
type : "post",
url : "/department/tree",
dataType : "text",
data : {'UId':id,'UCompany_id':company_id,'SEdit':'SEdit','add_user':'add_user','User[t_nickname]':t_nickname,'User[t_realname]':t_realname,'User[t_email]':t_email,'User[t_mobile]':t_mobile},
success : function(data){
$('#href_data').html(data);
}
});
}
}
注释:三个url都是一样的,这就是Ajax的特别之处,不管页面数据怎么变化,url地址还是一样的。
3、PHP部分。
列表-添加员工展示视图
if(!empty($post['AddUser'])){
$Data=$this->actionTreeUpdate("",$user1->company_id,""); //获取员工,员工详细信息,部门实例化的model
return $this->renderPartial('tree_update', [
'model' => $Data['model'],
'model_1' => $Data['model_'],
'departar'=>$Data['departar'],
'positionar'=>$Data['positionar'],
'users' => $Data['users'],
'UId'=>'', //添加不需要传
'UCompany_id'=>"", //添加不需要传
'add_user'=>"add_user"
]);
}
department里的tree方法主要代码,验证保存数据在这里
if(!empty($post['SEdit'])){
$UId=$post['UId'];
$UCompany_id=$post['UCompany_id'];
$SEdit=$post['SEdit'];
$add_user=!empty($post['add_user'])?$post['add_user']:"";
unset($post['SEdit'],$post['UId'],$post['UCompany_id'],$post['add_user']);
$Data=$this->actionTreeUpdate($UId,$UCompany_id,$post);
$class = $this->userModels;
$Class_= $this->UserProfile;
//判断是否为添加员工
if(!empty($add_user)){
$UId=0;
}
$model_2 = $class::findIdentity($UId);
$model_3=$Class_::findOne($UId);
//列表-添加员工
$t_id1="";
$t_id=!empty($model_2->attributes['t_id'])?$model_2->attributes['t_id']:"";
if(empty($model_2)){
$model_2 = new $class;
$post['_csrf']=Yii::$app->request->getCsrfToken();
$depart=!empty($post['department_ids'])?$post['department_ids']:array();
$model_2->parent_id=!empty($post['User']['parent_id'])?$post['User']['parent_id']:"";
if($depart)
{
$model_2->department_id1=!empty($depart[0])?$depart[0]:0;
$model_2->department_id2=!empty($depart[1])?$depart[1]:0;
$model_2->department_id3=!empty($depart[2])?$depart[2]:0;
$model_2->department_ids=join(',',$depart);
}else{
$model_2->department_ids="";
}
$model_2->t_password=base64_encode($user1->t_password);
$model_2->t_state=0;
$model_2->t_status=1;
$model_2->company_id= $user1->company_id;
if($model_2->load($post) && $model_2->save()){
//插入返回员工id
$t_id1=$model_2->attributes['t_id'];
$Data='success';
}
}
//验证通过,选择职位后保存
if($model_2->load($post) && $model_2->validate() && !empty($post['UserProfile']['position'])){
$Data='success';
}
if($model_2->load($post) && $model_2->validate() && $Data=='success'){
if(empty($model_3) && !empty($t_id1)){
$model_3 = new $Class_;
$model_3->uid=$t_id1;
}
//保存职位
if(!empty($post['UserProfile']['position']) && $model_3->load($post))
{
$posar=!empty($post['UserProfile']['position'])?$post['UserProfile']['position']:array();
if($posar)
{
$model_3->position_id1=!empty($posar[0])?$posar[0]:0;
$model_3->position_id2=!empty($posar[1])?$posar[1]:0;
$model_3->position_id3=!empty($posar[2])?$posar[2]:0;
$model_3->position_ids=$posar?join(',',$posar):'';
$model_3->position=Position::getpos_name($posar);
}else{
$model_3->position_id1="";
}
$model_3->save();
}else{
$posar=!empty($post['UserProfile']['position'])?$post['UserProfile']['position']:array();
if(empty($posar)){
//判断个人详细信息表是否有值,有值直接替换,无值重新实例化
$model_3=$Class_::find()->where(['uid'=>$t_id])->one();
if(empty($model_3)){
$model_3 = new $Class_;
$model_3->uid=$t_id;
}
$model_3->position_id1=0;
$model_3->position_id2=0;
$model_3->position_id3=0;
$model_3->position_ids=0;
$model_3->save();
}
}
return $this->renderPartial('tree_list', [
'dataProvider' => $dataProvider,
'searchModel' => $searchModel,
'idField' => $this->idField,
'usernameField' => $this->usernameField,
'extraColumns' => $this->extraColumns,
'data'=>$data_1,
'data_2' => $html,
'model_'=>$model,
'department_ids1' =>!empty($Dep->id)?$Dep->id:"",
'company_id1'=>$UCompany_id,
]);
注释:1、Yii2 Csrf自动验证功能只有使用$model_2->validate()或者$model_2->save()才能够触发。
2、使用$model->load($post);$post的数组格式如下几种类型,必须的
Array
(
[User] => Array
(
[parent_id] =>
[t_nickname] => 测试
[t_realname] => 小凌
[t_mobile] =>
[t_email] => [email protected]
)
[department_ids] => Array
(
[0] => 14
[1] => 15
)
[UserProfile] => Array
(
[position]=>Array
(
[0] => 5
[1] => 10
)
)
[_csrf] => LkU4dlE2amdIdVI9AHVZJGYdC058BCctejRbPhdxDQJlBmI3MFkpNQ==
)
常见问题
1、Yii2自带的Csrf验证不起作用。
检查方向:
1.1、是否使用了$model_2->validate()或者$model_2->save(),触发Csrf验证必须有。
1.2、Ajax传过来的数组格式是不是正确的,上面有说明。
1.3、查看控制台报错信息,哪报错从哪找原因。
2、提交表单的时候使用submitButton,使用submitButton的话没法实现无刷新。控制器所需的数据全部都是通过JS获取,Ajax传的方式得到。
3、一大模块全部实现无刷新,有些方法代码量会很大,得把握好它们之间的相关性和其它互不影响性。
相关资料
大侠给讲解一下, $model->load() 与 validate():http://www.yiichina.com/question/955
使用表单:http://www.yiibai.com/yii2/start-forms.html
Ajax+PHP实现动态无刷新技术:http://www.cnblogs.com/freespider/archive/2012/04/11/2442138.html