AngularJS+ThinkPHP實例教程


總體思路

thinkphp通過RESTful方式提供數據給angular,前端(包括模板頁面)全部由angular來接管。

示例

實現一個用戶管理模塊,走通增刪改查4個操作,通過該示例,演示如何在thinkphp中使用AngularJS

一. 准備工作

1. 加載所需的js和css文件

angular.min.js angular    核心庫文件

angular-ui-router.min.js angular    路由插件

angular-resource.min.js    負責與服務端restful交互的插件

layer    彈窗插件,該插件依賴於jQuery-1.10.1.min.js和jquery-ui.min.js兩個庫文件以及一個jquery-ui.min.css樣式表

bootstrap.min.css bootstrap    核心樣式表

文件結構如下:

2. 引導頁

准備一個angular引導頁,angular通過該引導頁開啟一個angular應用,后續所有的操作,都是基於該引導頁進行的。

友情提醒:

這里的引導頁,其實就是我們應用的默認頁面,具體到thinkphp中,指的就是DEFAULT_MODULE/DEFAULT_CONTROLLER/DEFAULT_ACTION對應的模板文件。第一次訪問應用時,thinkphp控制器會定向到該頁面,之后的模板頁面,全部由angular接管,跟thinkphp的模板引擎就沒有半點關系了。

3. 應用首頁

在引導頁中通過<div ui-view="main"></div>包含我們的應用首頁。便簽的含義后面會有解釋。

注意:這一步不是必須的,因為你也可以把應用首頁的內容,全部放在引導頁中,但是這樣做就不夠友好了。

4. 搭建RESTFul環境

為了能夠使用$resource,我們必須讓服務端按照RESTful的方式來工作,否則,$resource就無法發揮其作用了。

thinkphp中內置了對RESTful的支持,使用方式也很簡單,就是讓某個Controller繼承自RestController,然后按照一定的規則來編寫資源方法即可。但是它有一個缺點,thinkphp內置的RESTful對資源的訪問方式不夠友好,其訪問資源的URL結構如下:

/模塊名稱/控制器名稱/資源名稱

基於此,我們考慮使用thinkphp的路由功能,來實現對RESTful的支持,使用thinkphp的路由功能時,控制器是沒有必要繼承RestController的,但為了尊重tp的勞動成果,同時,為了兼顧友好的資源訪問方式,最終,我使用的是RestController+路由兩者結合的方式。

補充:

資源方法的命名規則為:資源名稱_REST類型

REST類型有如下幾種:get,post,put,delete

5. 創建數據庫

創建用戶表,並初始化一些數據

 

[sql]  view plain  copy
 
  1. DROP TABLE IF EXISTS `an_user`;  
  2. CREATE TABLE `an_user` (  
  3.   `id` int(10) unsigned NOT NULL AUTO_INCREMENT,  
  4.   `user_id` int(10) unsigned NOT NULL COMMENT '用戶id',  
  5.   `user_name` varchar(100) NOT NULL COMMENT '用戶名稱',  
  6.   `email` varchar(255) DEFAULT NULL COMMENT '郵箱地址',  
  7.   `tel` varchar(255) DEFAULT NULL COMMENT '手機號碼',  
  8.   `weixin` varchar(255) DEFAULT NULL COMMENT '微信號',  
  9.   `qq` varchar(255) DEFAULT NULL COMMENT 'qq號碼',  
  10.   PRIMARY KEY (`id`)  
  11. ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='用戶表';  
  12.   
  13. INSERT INTO `an_user` VALUES ('1', '1', 'demo1', 'demo1@qq.com', '13100000000', 'weixin_test1', '123456');  
  14. INSERT INTO `an_user` VALUES ('2', '2', 'demo2', 'demo2@qq.com', '13100000001', 'weixin_test2', '123456');  
  15. INSERT INTO `an_user` VALUES ('3', '3', 'demo3', 'demo3@qq.com', '13100000002', 'weixin_test3', '123456');  
  16. INSERT INTO `an_user` VALUES ('4', '4', 'demo4', 'demo4@qq.com', '13100000003', 'weixin_test4', '123456');  
  17. INSERT INTO `an_user` VALUES ('5', '5', 'demo5', 'demo5@qq.com', '13100000004', 'weixin_test5', '123456');  
  18. INSERT INTO `an_user` VALUES ('6', '6', 'demo6', 'demo6@qq.com', '13100000005', 'weixin_test6', '123456');  
  19. INSERT INTO `an_user` VALUES ('7', '7', 'demo7', 'demo7@qq.com', '13100000006', 'weixin_test7', '123456');  
  20. INSERT INTO `an_user` VALUES ('8', '8', 'demo8', 'demo8@qq.com', '13100000007', 'weixin_test8', '123456');  
  21. INSERT INTO `an_user` VALUES ('9', '9', 'demo9', 'demo9@qq.com', '13100000008', 'weixin_test9', '123456');  

二. 開始吧

 

1. 創建引導頁

前面說過,引導頁其實就是我們應用的默認頁面,在我的項目中,默認頁面的文件路徑為:

/tpl/Admin/Index/index.html

在該文件中,加入如下內容:

 

[html]  view plain  copy
 
  1. <!DOCTYPE html>  
  2. <html ng-app="antp">  
  3.     <head>  
  4.         <meta charset="utf-8" />  
  5.         <title></title>  
  6.         <meta content="width=device-width, initial-scale=1.0" name="viewport" />  
  7.         <meta content="" name="author" />  
  8.         <link href="/public/css/bootstrap.min.css" rel="stylesheet" type="text/css" media="all">  
  9.         <link href="/public/css/jquery-ui.min.css" rel="stylesheet" type="text/css" media="all">  
  10.         <script type="text/javascript" src="/public/js/angular.min.js"></script>  
  11.         <script type="text/javascript" src="/public/js/angular-resource.min.js"></script>  
  12.         <script type="text/javascript" src="/public/js/angular-ui-router.min.js"></script>  
  13.         <script type="text/javascript" src="/public/js/jquery-1.10.1.min.js"></script>  
  14.         <script type="text/javascript" src="/public/js/jquery-ui.min.js"></script>  
  15.         <script type="text/javascript" src="/public/js/layer/layer.js"></script>  
  16.         <script type="text/javascript" src="/config.js"></script>  
  17.         <script type="text/javascript" src="/app.js"></script>  
  18.         <link rel="shortcut icon" href="favicon.ico" />  
  19.     </head>  
  20.     <body>  
  21.         <div class="container-fluid">  
  22.             <div class="row clearfix">  
  23.                 <div class="col-md-12" style="float: none;display: block;margin-left: auto;margin-right: auto;">  
  24.                     <div ui-view="main"></div>  
  25.                 </div>  
  26.             </div>  
  27.         </div>  
  28.     </body>  
  29. </html>  

 

頁面中加載的js和css,除了config.js和app.js,其它的在前面都已經作了說明,這兩個后面也會說到。

注意html標簽中的ng-app指令,該指令的內容,就是angular要開啟的應用名稱。

2. 開啟應用

之前的index.html文件中,通過ng-app指令定義了一個應用名稱,定義完之后,我們還需要開啟它,開啟的方式也很簡單,我們需要創建一個js文件,名稱為app.js,在里面先加入如下內容:

 

 

[javascript]  view plain  copy
 
  1. var app = angular.module("antp",["ui.router","ngResource"]);  

 

其中,module的第二個參數,是angular的一些依賴包。

3. 配置路由

使用ui.router的路由功能,通過config預先配置好訪問url、該url對應的視圖模板以及控制器等信息,同樣是在app.js文件中,繼續加入如下內容:

 

[javascript]  view plain  copy
 
  1. app.config(function($stateProvider, $urlRouterProvider, $locationProvider) {  
  2.     //啟用HTML5模式的路由,該模式下會去除URL中的#號  
  3.     //$locationProvider.html5Mode(true);  
  4.     //默認頁面,所有請求不到的資源,都會轉向到這個URL  
  5.     $urlRouterProvider.otherwise("/index");  
  6.     $stateProvider.state("user", {  
  7.         url: "/user",  
  8.         views: {  
  9.             main: {  
  10.                 templateUrl: "tpl/Admin/User/index.html",  
  11.                 controller: "UserCtroller"  
  12.             }  
  13.         }  
  14.     }).state("index", {  
  15.         url: "/index",  
  16.         views: {  
  17.             main: {  
  18.                 templateUrl: "tpl/Admin/Index/main.html",  
  19.                 controller: "MainCtroller"  
  20.             }  
  21.         }  
  22.     }).state("user-add", {  
  23.         url: "/user/add",  
  24.         views: {  
  25.             main: {  
  26.                 templateUrl: "tpl/Admin/User/add.html",  
  27.                 controller: "UserFormCtroller"  
  28.             }  
  29.         }  
  30.     }).state("user-edit", {  
  31.         url: "/user/edit/:user_id",  
  32.         views: {  
  33.             main: {  
  34.                 templateUrl: "tpl/Admin/User/add.html",  
  35.                 controller: "UserFormCtroller"  
  36.             }  
  37.         }  
  38.     });  
  39. });  

 

還記得之前引導頁中的ui-view="main"嗎?ui-view的名字,就是state中配置的views下面的屬性名稱。也許你會說,config里面配置了那么多的main,它是如何知道找的是哪個main?仔細看下代碼就知道了。

注意1:控制器controller我們並沒有手動寫在某個頁面的標簽上,而是統一配置在了config里。

注意2:每個視圖模板必須要對應一個控制器,並且這個控制器必須要被創建,否則,該視圖將無法展示。

注意3:不要把此處的控制器和thinkphp里面的控制器搞混,事實上,他們兩者之間沒有任何關系。

4. 引入每個模塊下的js

這里為了便於代碼管理與維護,我在每個模塊下面創建了一個js文件,每個模塊的js完成特定的功能,這些js同樣需要在app.js中被引入。

 

[javascript]  view plain  copy
 
  1. document.write('<script type="text/javascript" src="/tpl/Admin/Index/main.js"></script>');  
  2. document.write('<script type="text/javascript" src="/tpl/Admin/User/user.js"></script>');  

 

5. 瀏覽器訪問

在瀏覽器地址欄中,輸入http://local.antp,將會看到如下效果:

仔細觀察下地址欄,我們輸入的是http://local.antp,但是卻自動變成了http://local.antp/#/index,知道為什么嗎?

首頁,引導頁中的<div ui-view="main"></div>,表示我需要路由到main這個視圖,但是,我們輸入的地址http://local.antp中,並沒有告訴angular到底是哪個main,發生這種情況的時候,angular就會定向到默認的視圖,默認的視圖由$urlRouterProvider.otherwise("/index");這一句來指定。

由於/index指向的是tpl/Admin/Index/main.html,所以此處就會把main.html對應的內容展示出來。

main.html文件內容:

 

 

[html]  view plain  copy
 
  1. <div ng-include="'tpl/Admin/Public/header.html'"></div>  
  2. <blockquote>  
  3.     <p>  
  4.         我是默認頁面,所有請求不到的資源,都會到我這里來...  
  5.     </p>  
  6. </blockquote>  

 

其中,通過ng-include指令,引入了一個公共導航頁面,ng-include指令中雙引號內的單引號不可少,內容如下:

 

[html]  view plain  copy
 
  1. <div style="margin-top:20px;">  
  2.     <nav class="navbar navbar-default" role="navigation">  
  3.         <div class="navbar-header">  
  4.             <class="navbar-brand" href="#">Brand</a>  
  5.         </div>  
  6.         <div class="collapse navbar-collapse">  
  7.             <ul class="nav navbar-nav">  
  8.                 <li class="active">  
  9.                      <ui-sref="user">用戶管理</a>  
  10.                 </li>  
  11.             </ul>  
  12.               
  13.         </div>  
  14.     </nav>  
  15. </div>  

 

6. 用戶列表

點擊導航中的【用戶管理】,即可跳轉到用戶列表頁面,如下:

該列表對應的視圖文件為tpl/Admin/User/index.html,內容如下:

 

[html]  view plain  copy
 
  1. <div ng-include="'tpl/Admin/Public/header.html'"></div>  
  2. <button type="button" class="btn btn-primary" ng-click="addAction()">新增</button>  
  3. <table class="table table-bordered table-striped" style="margin-top:15px;">  
  4.     <thead>  
  5.         <tr>  
  6.             <th>用戶名</th>  
  7.             <th>郵箱</th>  
  8.             <th>手機號</th>  
  9.             <th>微信</th>  
  10.             <th>QQ</th>  
  11.             <th>操作</th>  
  12.         </tr>  
  13.     </thead>  
  14.     <tbody>  
  15.         <tr ng-repeat="user in data.user">  
  16.             <td>  
  17.                 <ui-sref="user-edit({user_id:user.user_id})" ng-bind="user.user_name"></a>  
  18.             </td>  
  19.             <td ng-bind="user.email"></td>  
  20.             <td ng-bind="user.tel"></td>  
  21.             <td ng-bind="user.weixin"></td>  
  22.             <td ng-bind="user.qq"></td>  
  23.             <td>  
  24.                 <button type="button" class="btn btn-link">  
  25.                     <ui-sref="user-edit({user_id:user.user_id})">修改</a>  
  26.                 </button>  
  27.                 <button type="button" class="btn btn-link" ng-click="deleteAction(user.user_id)">刪除</button>  
  28.             </td>  
  29.         </tr>  
  30.     </tbody>  
  31. </table>  

 

為了能夠從后端拿到數據,我們需要創建一個$resource資源,創建方式如下:

 

[javascript]  view plain  copy
 
  1. //通過factory創建一個service,該service通過$resource返回了一個資源對象  
  2. //$resource負責與支持restful的服務端進行數據交互  
  3. app.factory("UserService", function($resource) {  
  4.     return $resource(globalConfig.API.URL + "users/:id", {  
  5.        id: "@id"  
  6.     },  
  7.     {  
  8.         //query方法要求服務端返回的數據格式為數組,如果返回的是非數組格式,需要在transformResponse函數中作轉換處理  
  9.         query: {  
  10.             method: "GET",  
  11.             isArray: true,  
  12.             transformResponse: function(data) {  
  13.                 return JSON.parse(data);  
  14.             }  
  15.         },  
  16.         update: {  
  17.             method: "PUT"  
  18.         }  
  19.     });  
  20. });  

 

這里,創建了一個名字為UserService的資源,然后,我們還需要創建一個控制器,將UserService資源注入進去,代碼如下:

 

[javascript]  view plain  copy
 
  1. //用戶列表Ctroller  
  2. app.controller('UserCtroller', function($scope, $state, UserService) {  
  3.     $scope.data = {};  
  4.     //獲取用戶列表  
  5.     UserService.query().$promise.then(  
  6.         function(data){  
  7.             //將查詢結果賦值給data.user,模板中可以對data.user變量進行遍歷  
  8.             $scope.data.user = data;  
  9.         },  
  10.         function(error) {  
  11.             console.log("An error occurred", error);  
  12.         }  
  13.     );  
  14.     $scope.addAction = function() {  
  15.         $state.go("user-add");  
  16.     };  
  17.     $scope.deleteAction = function(user_id){  
  18.         layer.confirm("確定要刪除該用戶嗎", {  
  19.             btn: ['確定','取消']  
  20.         }, function(index){  
  21.             layer.close(index);  
  22.             UserService.remove({id:user_id}).$promise.then(  
  23.                 function(res){  
  24.                     if(res.status){  
  25.                         $state.go("user",null,{  
  26.                             reload:true  
  27.                         });  
  28.                     }else{  
  29.                           
  30.                     }  
  31.                 },  
  32.                 function(error) {  
  33.                     console.log("An error occurred", error);  
  34.                 }  
  35.             );  
  36.         });  
  37.     }  
  38. });  

 

 

通過UserService的query方法,獲取用戶列表信息。

 

7. 新增用戶

點擊列表上的【新增】,將會通過$state.Go("user-add");跳轉到新增頁面:

該頁面對應的視圖文件為tpl/Admin/User/add.html,內容如下:

 

[html]  view plain  copy
 
  1. <div ng-include="'tpl/Admin/Public/header.html'"></div>  
  2. <form class="form-horizontal" role="form" name="userForm">  
  3.     <div class="form-group">  
  4.          <label class="col-sm-3 control-label">用戶名</label>  
  5.          <div class="col-sm-4">  
  6.             <input type="text" class="form-control" ng-model="user.user_name"/>  
  7.          </div>  
  8.     </div>  
  9.     <div class="form-group">  
  10.          <label class="col-sm-3 control-label">郵箱</label>  
  11.          <div class="col-sm-4">  
  12.             <input type="email" class="form-control" ng-model="user.email"/>  
  13.          </div>  
  14.     </div>  
  15.     <div class="form-group">  
  16.          <label class="col-sm-3 control-label">手機號</label>  
  17.          <div class="col-sm-4">  
  18.             <input type="tel" class="form-control" ng-model="user.tel"/>  
  19.          </div>  
  20.     </div>  
  21.     <div class="form-group">  
  22.          <label class="col-sm-3 control-label">微信號</label>  
  23.          <div class="col-sm-4">  
  24.             <input type="text" class="form-control" ng-model="user.weixin"/>  
  25.          </div>  
  26.     </div>  
  27.     <div class="form-group">  
  28.          <label class="col-sm-3 control-label">QQ</label>  
  29.          <div class="col-sm-4">  
  30.             <input type="text" class="form-control" ng-model="user.qq"/>  
  31.          </div>  
  32.     </div>  
  33.     <div class="col-sm-offset-3 col-sm-4">  
  34.         <button type="submit" class="btn btn-primary" ng-click="submitAction(userForm)">  
  35.             保存  
  36.         </button>  
  37.         <button type="button" class="btn btn-default" ng-click="cancelAction()">  
  38.             返回  
  39.         </button>  
  40.     </div>  
  41. </form>  

 

 

該頁面中用到了angular的ng-model指令,ng-model指令用於數據的雙向綁定,注意下表單中類似於user.user_name的東西,首先,controller中會通過點號前面的user獲取表單數據,表單數據就是點號后面若干個類似於user_name的對應的內容的組合。

js代碼:

 

 

[html]  view plain  copy
 
  1. //新增和修改用戶Ctroller  
  2. app.controller('UserFormCtroller', function($scope, $state, $stateParams, UserService) {  
  3.     if($stateParams.user_id){  
  4.         var user_id = $stateParams.user_id;  
  5.         var param = {id:user_id};  
  6.         //獲取指定用戶  
  7.         UserService.get(param).$promise.then(  
  8.             function(res) {  
  9.                 $scope.user = res;  
  10.             },  
  11.             function(error) {  
  12.                 console.log("An error occurred", error);  
  13.             }  
  14.         );  
  15.     }  
  16.     $scope.submitAction = function(userForm) {  
  17.         if(!userForm.$valid){  
  18.             return false;  
  19.         }  
  20.         if($stateParams.user_id){  
  21.             //更新用戶信息  
  22.             UserService.update(param,$scope.user).$promise.then(  
  23.                 function(res) {  
  24.                     if(res.status){  
  25.                         $state.go("user");  
  26.                     }else{  
  27.                           
  28.                     }  
  29.                 },  
  30.                 function(error) {  
  31.                     console.log("An error occured", error);  
  32.                 }  
  33.             );  
  34.         }else{  
  35.             //新增  
  36.             UserService.save($scope.user).$promise.then(  
  37.                 function(res) {  
  38.                     if(res.status){  
  39.                         $state.go("user");  
  40.                     }else{  
  41.                           
  42.                     }  
  43.                 },  
  44.                 function(error) {  
  45.                     console.log("An error occured", error);  
  46.                 }  
  47.             );  
  48.         }  
  49.     };  
  50.     $scope.cancelAction = function() {  
  51.         $state.go("user");  
  52.     };  
  53. });  

通過UserService的save方法來新增一個用戶,而update方法則負責用戶信息的修改。

注意:在修改用戶的信息之前,我們需要通過UserService的get方法把用戶已有的信息填充到頁面上。

8. 修改用戶

點擊列表上的【修改】鏈接,進入到用戶修改頁面:

通過列表中的ui-sref="user-edit({user_id:user.user_id})"這一句,即可實現頁面的跳轉,同時,帶上了user_id這個參數,可以在控制器中注入$stateParams來獲取此處的參數,代碼在前面已經貼過了。

9. 刪除用戶

點擊列表上的【刪除】,將會彈出一個刪除確認的提示,如圖:

點擊【確定】后,即可通過UserService的remove方法來刪除該用戶。

注意:刪除一個資源,也可以使用delete方法,但由於delete在有些瀏覽器中被當成了關鍵字,所以使用上需要寫成UserService["delete"]這種格式。

10. thinkphp路由配置

 

[php]  view plain  copy
 
  1. 'URL_ROUTER_ON' => true  
[php]  view plain  copy
 
  1. 'URL_ROUTE_RULES' => array(  
  2.         array('api/users/:id','Admin/User/user_get','',array('method'=>'get')),  
  3.         //注意:列表記錄對應的路由必須要放在單條記錄路由的后面,否則無法獲取單條記錄  
  4.         array('api/users','Admin/User/user_get','',array('method'=>'get')),  
  5.         array('api/users','Admin/User/user_post','',array('method'=>'post')),  
  6.         array('api/users/:id','Admin/User/user_put','',array('method'=>'put')),  
  7.         array('api/users/:id','Admin/User/user_delete','',array('method'=>'delete')),  
  8.     )  

 

11. 后端代碼

 

[php]  view plain  copy
 
  1. //查詢用戶列表或單條記錄  
  2.     public function user_get(){  
  3.         $id = I('id');  
  4.         if ($id) {  
  5.             $where = array('user_id' => $id);  
  6.             $users = DBUtil::queryRow($this->userModel,$where);  
  7.         }else{  
  8.             $users = DBUtil::queryList($this->userModel);  
  9.         }  
  10.         $this->response($users,'json');  
  11.     }  
  12.       
  13.     //新增用戶  
  14.     public function user_post(){  
  15.         $user_id = DBUtil::getMaxKey($this->userModel, 'user_id');  
  16.         //angular默認post過來的數據類型為Content-Type:application/json; charset=utf-8  
  17.         $userData = json_decode(file_get_contents('php://input'),true);  
  18.         $userData['user_id'] = $user_id;  
  19.         $result = DBUtil::add($this->userModel, $userData);  
  20.         $this->response(array('status'=>$result),'json');  
  21.     }  
  22.       
  23.     //修改用戶  
  24.     public function user_put(){  
  25.         $user_id = $_REQUEST['id'];  
  26.         //angular默認post過來的數據類型為Content-Type:application/json; charset=utf-8  
  27.         $userData = json_decode(file_get_contents('php://input'),true);  
  28.         $result = DBUtil::save($this->userModel, array('user_id' => $user_id), $userData);  
  29.         if ($result !== false) {  
  30.             $result = true;  
  31.         }  
  32.         $this->response(array('status'=>$result),'json');  
  33.     }  
  34.       
  35.     //刪除用戶  
  36.     public function user_delete(){  
  37.         $user_id = $_REQUEST['id'];  
  38.         $result = DBUtil::delete($this->userModel, array('user_id' => $user_id));  
  39.         $this->response(array('status'=>$result),'json');  
  40.     }  

三. 去除URL中的#號

 

默認情況下,angular通過URL中的#號來識別資源路徑類別,凡是帶#號的資源,統一由angular調配,而不帶#號的資源,由服務端調配,如果需要去除#號,可以參考以下步驟:

1. 啟用HTML5模式

首頁需要在config中注入$locationProvider,然后通過$locationProvider.html5Mode(true);來啟用html5模式的路由。

2. 加入base標簽

在引導頁中加入<base href="/">

3. 后端thinkphp配置

在控制器中增加一個_empty方法,在該方法中,跳轉到首頁。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM