環境:ThinkPHP3.2.3,jQuery3.2
前言:
在一般的網站中,都需要用到jquery或者其他框架(比如angular)來處理前后端數據交互,thinkphp在后台也內置了一些函數用於數據交互(比如ajaxReturn())。本文的目的是打通使用ajax在jquery和thinkphp之間的前后端數據交互過程。
正文:
一、thinkphp關於ajax的介紹
1.1 ajaxReturn:
\Think\Controller類提供了ajaxReturn方法用於AJAX返回數據給客戶端(視圖、模板、js等)。並且支持JSON、JSONP、XML和EVAL四種方式給客戶端接受數據(默認JSON)。
配置方式:convention.php中定義了默認編碼類型為DEFAULT_AJAX_RETURN => 'JSON',
分析:ajaxReturn()調用了json_encode()將數值轉換成json數據存儲格式,常用的數值是數組。
注意:The value being encoded can be any type except a resource(資源文件).All string data must be UTF-8 encoded.
舉例:
$data['status'] = 1; $data['content'] = 'content'; $this->ajaxReturn($data);
1.2 請求類型:
系統內置了一些常量用於判斷請求類型,比如:
常量 說明
IS_GET 判斷是否是GET方式提交
IS_POST 判斷是否是POST方式提交
IS_PUT 判斷是否是PUT方式提交
IS_DELETE 判斷是否是DELETE方式提交
IS_AJAX 判斷是否是AJAX提交
REQUEST_METHOD 當前提交類型
目的:一方面可以針對請求類型作出不同的邏輯處理,另外一方面可以過濾不安全的請求。
使用方法:
class UserController extends Controller{ public function update(){ if (IS_POST){ $User = M('User'); $User->create(); $User->save(); $this->success('保存完成'); }else{ $this->error('非法請求'); } return 0; } }
1.3 跳轉和重定向:
功能比較雞肋,在ajax異步交互局部刷新中,不需要有文字提示的跳轉。(鏈接:
http://document.thinkphp.cn/manual_3_2.html#page_jump_redirect)
二、jQuery Ajax的介紹:
2.1 官網關於jQuery.ajax()的介紹:
jQuery.ajax() 方法用於執行 AJAX(異步 HTTP)請求。(鏈接:
http://www.jquery123.com/jQuery.ajax/)
語法:$.ajax({name:value, name:value, ... }),該參數規定 AJAX 請求的一個或多個名稱/值對。
常見參數:

type (默認: 'GET') 類型: String 請求方式 ("POST" 或 "GET"), 默認為 "GET"。注意:其它 HTTP 請求方法,如 PUT 和 DELETE 也可以使用,但僅部分瀏覽器支持。 url (默認: 當前頁面地址) 類型: String 發送請求的地址。 async (默認: true)(1.8版本已棄用) 類型: Boolean 默認設置下,所有請求均為異步請求(也就是說這是默認設置為 true )。如果需要發送同步請求,請將此選項設置為 false 。 data 類型: Object, String 發送到服務器的數據。將自動轉換為請求字符串格式。GET 請求中將附加在 URL 后面。查看 processData 選項說明,以禁止此自動轉換。對象必須為"{鍵:值}"格式。如果這個參數是一個數組,jQuery會按照traditional 參數的值, 將自動轉化為一個同名的多值查詢字符串(查看下面的說明)。注:如 {foo:["bar1", "bar2"]} 轉換為 '&foo=bar1&foo=bar2'。 dataType (默認: Intelligent Guess (xml, json, script, or html)) 類型: String 預期服務器返回的數據類型。如果不指定,jQuery 將自動根據 HTTP 包 MIME 信息來智能判斷,比如XML MIME類型就被識別為XML。在1.4中,JSON就會生成一個JavaScript對象,而script則會執行這個腳本。隨后服務器端返回的數據會根據這個值解析后,傳遞給回調函數。舉例: "json": 把響應的結果當作 JSON 執行,並返回一個JavaScript對象。在 jQuery 1.4 中,JSON 格式的數據以嚴格的方式解析,如果格式有錯誤,jQuery都會被拒絕並拋出一個解析錯誤的異常。(見json.org的更多信息,正確的JSON格式。) error 類型: Function( jqXHR jqXHR, String textStatus, String errorThrown ) 請求失敗時調用此函數。有以下三個參數:jqXHR (在 jQuery 1.4.x前為XMLHttpRequest) 對象、描述發生錯誤類型的一個字符串 和 捕獲的異常對象。如果發生了錯誤,錯誤信息(第二個參數)除了得到null之外,還可能是"timeout", "error", "abort" ,和 "parsererror"。 當一個HTTP錯誤發生時,errorThrown 接收HTTP狀態的文本部分,比如: "Not Found"(沒有找到) 或者 "Internal Server Error."(服務器內部錯誤)。 從jQuery 1.5開始, 在error設置可以接受函數組成的數組。每個函數將被依次調用。 注意:此處理程序在跨域腳本和JSONP形式的請求時不被調用。這是一個 Ajax Event。 success 類型: Function( Object data, String textStatus, jqXHR jqXHR ) 請求成功后的回調函數。這個函數傳遞3個參數:從服務器返回的數據,並根據dataType參數進行處理后的數據,一個描述狀態的字符串;還有 jqXHR(在jQuery 1.4.x前為XMLHttpRequest) 對象 。在jQuery 1.5, 成功設置可以接受一個函數數組。每個函數將被依次調用。這是一個 Ajax Event 其他jQuery-ajax-settings,詳見:http://www.jquery123.com/#jQuery-ajax-settings
舉例:
在js中把id作為數據發送到服務器, 保存一些數據到服務器上, 一旦請求完成就通知用戶。 如果請求失敗,則提醒用戶。
var menuId = $("ul.nav").first().attr("id"); var request = $.ajax({ url: "script.php", type: "POST", data: {id : menuId}, dataType: "html" }); request.done(function(msg) { $("#log").html( msg ); }); request.fail(function(jqXHR, textStatus) { alert( "Request failed: " + textStatus ); });
注意:此處也可以在ajax()中使用success和error參數判斷請求結果成功還是失敗,並執行下一步操作。
2.2 js與json
2.2.1 json是什么:
JSON:JavaScript 對象表示法(JavaScript Object Notation)。是獨立於語言之外的存儲和交換文本信息的語法。
2.2.2 json和ajax的關系?
在上面關於jquery.ajax的介紹中提到了,json可以作為一個ajax函數的dataType,這樣數據就會通過json語法傳輸了。(鏈接:
http://www.cnblogs.com/haitao-fan/p/3908973.html)
在jquery的ajax函數中,只能傳入3種類型的數據:(鏈接:
http://www.cnblogs.com/haitao-fan/p/3908973.html)
>1.json字符串:"uname=alice&mobileIpt=110&birthday=1983-05-12"
>2.json對象:{uanme:'vic',mobileIpt:'110',birthday:'2013-11-11'}
>3.json數組:
[
{"name":"uname","value":"alice"},
{"name":"mobileIpt","value":"110"},
{"name":"birthday","value":"2012-11-11"}
]
2.2.3 json的編解碼和數據轉換:
2.2.2中提到的json對象是更方便與js數組、js字符串或php數組、php字符串進行數據轉化的json類型。下面以json對象為例講解一下json對象與js和php的數據類型轉化。
json對象轉化成數組:
<script type="text/javascript"> var jsonStr = '[{"id":"01","open":false,"pId":"0","name":"A部門"},{"id":"01","open":false,"pId":"0","name":"A部門"},{"id":"011","open":false,"pId":"01","name":"A部門"},{"id":"03","open":false,"pId":"0","name":"A部門"},{"id":"04","open":false,"pId":"0","name":"A部門"}, {"id":"05","open":false,"pId":"0","name":"A部門"}, {"id":"06","open":false,"pId":"0","name":"A部門"}]'; // var jsonObj = $.parseJSON(jsonStr); var jsonObj = JSON.parse(jsonStr) console.log(jsonObj) var jsonStr1 = JSON.stringify(jsonObj) console.log(jsonStr1+"jsonStr1") var jsonArr = []; for(var i =0 ;i < jsonObj.length;i++){ jsonArr[i] = jsonObj[i]; } console.log(typeof(jsonArr)) </script>
想要將表單數據提交到后台,需要先從表單獲取數據/數據集:
serialize和serializeArray的區別是serialize()獲取到序列化的表單值字符串,serializeArray()以數組形式輸出序列化表單值。舉例:
var serialize_string=$('#form').serialize();
得到:a=1&b=2&c=3&d=4&e=5
var serialize_string_array=$('#form').serializeArray();
得到:
[
{name: 'firstname', value: 'Hello'},
{name: 'lastname', value: 'World'},
{name: 'alias'}, // 值為空
]
相對來說,serializeArray()和最終想要得到的json數組更加相似。只不過需要將包含多個name-value形式json對象的json數組改寫成'first_name':'Hello'形式的json對象。
這里使用第一種方法舉例,傳入的參數是serializeArray()函數的結果,並且稍作優化,可以起名為change_serialize_to_json():
function change_serialize_to_json(serialize_objective_array) { var temp_json_object = {}; $.each(serialize_objective_array, function () { temp_json_object[this.name] = this.value; }); return temp_json_object; }
輸出:{"input1":"","textarea":"234","select":"1"}
2.2.4完整流程:
var serialize_array=$('#form').serializeArray()結果(結果是json對象數組):
Array [ Object, Object ]
var data=change_serialize_to_json(serialize_array)的結果是(以第二種轉換方法為例,結果是json對象):
Object {serial_number: "SN2", result: "非法" }
var json_data=JSON.stringify(data)(結果是json字符串):
{"serial_number":"SN2","result":"非法"}
在js端將表單數據轉化為json形式的其他函數:
將json字符串轉換為json對象:
eval("(" + status_process+ ")");
json字符串轉化成json對象:
// jquery的方法 var jsonObj = $.parseJSON(jsonStr) //js 的方法 var jsonObj = JSON.parse(jsonStr)
json對象轉化成json字符串:
//js方法 var jsonStr1 = JSON.stringify(jsonObj)
JSON.parse()用於從一個字符串中解析出json對象。JSON.stringify()相反,用於從一個對象解析出字符串。
str_replace() 函數用於替換掉字符串中的特定字符,比如替換掉數據轉換后多余的空格、'/nbsp'等
注意:serialize和serializeArray()函數在處理checkbox時存在無法獲取未勾選項的bug,需要自己編寫函數改寫原函數,舉例:
(鏈接:http://www.cnblogs.com/tangge/p/6554891.html)
//value賦值為off是因為正常的serializeArray()獲取到的勾選的checkbox值為on。 $.fn.my_serialize_array = function () { var my_serialize_array = this.serializeArray();//官方函數只包含勾選的checkbox,並且值默認為on var not_checked_object = $('input[type=checkbox]:not(:checked)', this);//this指調用這個函數的form $.each(not_checked_object, function () { if (!my_serialize_array.hasOwnProperty(this.name)) {//this指沒選中的一個object my_serialize_array.push({name: this.name, value: "off"}); } }); console.log(my_serialize_array); return my_serialize_array; };
三、使用js操作DOM實現異步局部刷新的完整流程:
實現局部刷新的途徑:
1、假設頁面有查詢form和結果table。
2、點擊查詢form的提交,觸發js自定義的submit事件,在submit函數中對獲取的表單數據檢測后如果符合要求就傳遞給控制器,控制器從數據庫獲取結果數組后返回給ajax的success。對返回給ajax的結果數組,可以創建一個refresh()函數,或直接在success中用jQuery(或其他js)操縱結果table(DOM),比如刪除tbody節點下的所有內容,並將結果數組格式化后添加到tbody下面。
舉例:
//1、php中的form表單 <div class="modal fade hide" id="add_engineer_modal" tabindex="-1" role="dialog"> ...... <form id="add_engineer_modal_form" class="form-horizontal"> <fieldset> ...... <button type="button" class="btn btn-primary" id="add_engineer_modal_submit" onclick="add_engineer_modal_submit()" >提交更改</button> ...... </fieldset> </form> </div>
//2、js校驗表單並發起ajax function add_engineer_modal_check_value() { //以edit_modal_check_value()為模板 var serialize_array_object = $("#add_engineer_modal_form").mySerializeArray();//針對checkbox優化的SerializeArray var data = change_serialize_to_json(serialize_array_object); var check_results = []; check_results['result'] = [];//保存錯誤信息 check_results['data'] = data;//保存input和select對象 //check_employee_number是自定義判斷員工號函數。 if (check_employee_number(data['employee_number']) == false) { check_results['result'].push("請輸入有效的員工號(可選)"); } return check_results; } function add_engineer_modal_submit() { var check_results = add_engineer_modal_check_value(); if (check_results['result'].length == 0) { //注意,是否要轉換上面的data,要取決於你想要用什么類型傳輸,用js對象傳輸更好,不需要stringify,也方便控制器處理。我的舊代碼有些累贅了。 var json_data = JSON.stringify(check_results['data']); //JSON.stringify() 方法將一個JavaScript值轉換為一個JSON字符串(ajax要求json對象或json字符串才能傳輸) $.ajax({ type: 'POST', url: add_engineer_url, //在php中全局定義url,方便使用thinkphp的U方法 data: {"json_data": json_data}, //ajax要求json對象或json字符串才能傳輸,json_data只是json字符串而已。如果上面用js對象傳輸,這里就寫成"data:json_data," dataType: "json", success: function (data) { console.log("數據交互成功"); }, error: function (data) { console.log("數據交互失敗"); } }); } else { //彈出錯誤提示alert } return 0; }
注意:此處的add_engineer_url一般定義在函數之前,由於我有很多函數可能要用到這個url,我一般寫在js最前面。當然也可以寫在php的head之內,那樣就可以使用U方法自動生成url。
3、控制器檢查數據操作數據庫並返回數組給js
public function add_engineer() { if (IS_AJAX) { $posted_json_data = I('post.json_data'); $posted_json_data_replace = str_replace('"', '"', $posted_json_data);//慎用str_replace,如果非要用最好在轉成數組之后,針對特定的item替換。舊代碼這樣寫不太好。 $posted_json_data_replace_array = (Array)json_decode($posted_json_data_replace);//一般不需要強制Array,json_decode的返回值就是Array,此處我是為了防止自己的serializeArray產生了特殊值影響到這里。最好還要有一些過濾函數過濾掉script、html元素等 //處理數據庫事務寫入,通過判斷寫入結果來區分ajaxReturn的結果 //可以將所有想要返回的數據放在一個數組中,比如新增的行id、插入數據庫的操作是否成功 //如果操作數據庫成功就返回如下結果。 $user_table->commit();//省略事務等操作 $data['result'] = true; $data['pk_user_id'] = $data_add_user_result; $this->ajaxReturn($data);//省略條件判斷,建議對正反條件都返回數組,比如$data['alert']記錄返回結果true還是false,如果是true,就把$data['result']=$result,返回$data。 } return 0; }
注意:一定不要在有ajaxReturn的函數中添加打印(echo、var_dump等),這會導致ajax error。如果要在F12下調試,可以注釋掉ajaxReturn,也可以直接看交互過程的錯誤提示。
4、改寫js:
在js的ajax中,如果整個ajax正常交互,就會走success函數,否則會走error函數,一般情況下,error出現的原因都是傳輸的數據不符合要求。
在success中的data就是ajaxReturn中傳輸的數組,舉例:
success: function (data) { if (data['result'] == false) { alert(data['alert']); } else { $('#add_engineer_modal').modal('hide'); $('#user_list_table tr').eq(0).after('<tr></tr>'); //這里就可以使用data['pk_user_id']了。 $('#user_list_table tr').eq(1).append('<td>'+data['pk_user_id']+'</td>'); } },
四、總結
整個過程是:
在php中編寫頁面中的表單、提交按鈕等;
在js中對php中的按鈕事件添加校驗和觸發函數,在js函數內,如果js對象的格式和內容正確就向控制器url(php中初始化)發起ajax請求;
控制器中的相應操作響應ajax請求,並判斷數據后做數據庫讀寫操作,然后對數據庫操作結果做出判斷,ajaxReturn返回js需要的數組;
當ajax成功返回時,js中ajax的success里面使用js重寫(或初始化)需要顯示的信息。
這樣就完成了ajax異步局部刷新。