angularjs + requirjs前端架构

在决定使用angularJS开发项目时,考虑到多人协作,模块化,扩展等等因素,决定引入requirJS实现js文件的异步加载,解决模块之间的依赖关系。

参考了一些关于angularJS项目的前端架构,都觉得不甚理想,幸运的是最终看到一篇关于angularJS如何集成requireJS的文章:

原文地址:如何将angularJS项目与requireJS集成

一、项目组织目录:

  • css 文件夹

  • img 文件夹

  • js 文件夹

 – appIndex 文件夹

  — api文件夹

  — controller 文件夹

     —- ctlModule.js

     —- mainController.js

     —- homeController.js

  — directives 文件夹

  — services 文件夹

  — app.js

  — index_main.js

  — router.js

 – appLogin 文件夹

  — …

  — 与appIndex类似

  — …

  • lib 文件夹

 – angular 文件夹

  — angular.js

  — angular-ui-router.js

  — …

 – bootstrap 文件夹

 – jquery 文件夹

 – require 文件夹

 – php 文件夹

 – template 文件夹

  — common 文件夹

  — tplIndex 文件夹

  — tplLogin文件夹

  • 404.html

  • favicon.ico

  • humans.txt

  • index.html

  • LICENSE.txt

  • login.html

  • server.js

二、组织目录说明

两个大的模块,一个appIndex,代表前台展示模块;一个appLogin,代表登录模块。appLogin与appIndex类似,以appIndex为例:

appIndex下的文件夹:

api: 与后端交互

controllers: 控制器

directives: 指令

services: 服务

appIndex下的文件:

app.js 定义整个项目的module

index_main.js 配置脚本文件,启动angular

router.js 定义路由

api下的文件:

apiModule.js 定义api的module

mainApi.js 加载所有的api文件

homeApi.js 一个普通的api文件

controllers下的文件:

ctlModule.js 定义控制器的module

mainController.js 加载所有的控制器文件

homeController.js 一个普通的控制器文件

directives下的文件:

dirModule.js 定义指令的module

mainDirective.js 加载所有的指令文件

headerDirective.js 一个普通的指令文件

services下的文件:

serModule.js 定义服务的module

mainService.js 加载所有的服务文件

homeService.js 一个普通的服务文件

三、命名约定

$stateProvider.state(‘home’,{
url: ‘/home’,
templateUrl:’../../template/tplIndex/home.html’,
controller:’homeCtl’
})
以homeController为例:

对应的模板为home.html

控制器名字:homeCtl

return ctl.controller(‘homeCtl’,function($scope,person,serGetTitle,testData){


});
})

homeCtl里使用的服务,看到ser开头的,如serGetTitle,对应到homeService.js里查找,看到Data结尾的,如testData,对应到homeApi.js里查找
四、代码分析

1、index.html 主要代码如下:

<!doctype html>






    <div>
        <ul>
            <li><a href="#/home">home</a></li>
        </ul>
    </div>

    <div ui-view></div>

    <rabbit-footer></rabbit-footer>

</body>


在首页index.html只需要引入requireJs库文件,并且指明入口函数main.js,采用手动启动angularjs应用,所以这里不用为首页添加ng-app=’rabbit’。

2、index_main.js 主要代码如下:

require.config({
baseUrl:’js’,
paths:{
jquery:’../lib/jquery/jquery-1.9.1’,
angular:’../lib/angular/angular’,
angularUiRouter:’../lib/angular/angular-ui-router’,
Restangular:’../lib/angular/restangular-1.5.2’,
lodash:’../lib/angular/lodash-1.8.3’,
app:’appIndex/app’,
router:’appIndex/router’,
mainController:’appIndex/controllers/mainController’,
ctlModule:’appIndex/controllers/ctlModule’,
homeController:’appIndex/controllers/homeController’,

},
shim:{
‘angular’:{
exports:’angular’
},
‘angularUiRouter’:{
deps: [‘angular’],
exports:’angularUiRouter’
},

},
urlArgs:”bust=” + (new Date()).getTime() //防止读取缓存,调试用
});

require([
‘jquery’,
‘angular’,
‘angularUiRouter’,
‘app’,
‘router’
], function ($,angular,angularUiRouter,app,router) {
$(document).ready(function(){
//采用手动启动angularjs应用,所以这里不用为首页添加ng-app=’rabbit’
angular.bootstrap(document, [‘rabbit’]);
});
});
3、两个重要的文件app.js和router.js

app.js 主要代码如下:

define([“angular”,’mainController’,’mainDirective’,’mainService’,’mainApi’],function(angular){
return angular.module(“rabbit”,[‘ui.router’,’app.ctlModule’,’app.dirModule’,’app.serModule’,’app.apiModule’]);
})
定义匿名模块:依赖的mainController和mainDirective主要是用来加载angular的控制器和指令

ui.router:一个基于ngRoute开发的第三方路由,作为框架额外的附加功能,它们都将以模块依赖的形式被引入

rabbit为自定义的模块名,依赖第三方路由模块ui.router,以及自定义的控制器模块,自定义的指令模块

这样做的目的是:在程序启动(bootstrap)的时候,加载依赖模块(如:ui.router),将所有挂载在该模块的服务(provider),指令(directive),过滤器(filter)等都进行注册,那么在后面的程序中便可以调用了。

router.js 主要代码如下:

define([‘app’],function(app){
return app.run([‘$rootScope’, ‘$log’, function($rootScope, $log){
$rootScope.$on(‘$stateChangeSuccess’, function(event, toState, toParams, fromState, fromParams){
$log.debug(‘successfully changed states’) ;
});
}])
.config(function($stateProvider, $urlRouterProvider, $locationProvider, $uiViewScrollProvider){
// 默认进入重定向
//$urlRouterProvider.otherwise(‘/index’);
$stateProvider.state(‘home’,{
url: ‘/home’,
templateUrl:’../../template/tplIndex/home.html’,
controller:’homeCtl’
})
})
});
配置了一个指定 /home 的路由。

4、控制器

mainController.js 主要代码如下:

define([‘homeController’,’indexController’], function() {});
主要用来加载各个控制器(所有的控制器都将在这个文件中被加载),我们可以有很多个控制器文件,按照具体需要进行添加。

homeController.js 主要代码如下:

define([‘ctlModule’,’jquery’],function(ctl,$){
return ctl.controller(‘homeCtl’,function($scope,serGetTitle,testData){
$scope.title = serGetTitle.title;
$scope.projects = testData.getMessages();
})
})
控制器homeCtl依赖于ctlModule模块,其实可以理解为app.ctlModule模块下的一个“方法”。

ctlModule.js 主要代码如下:

define([‘angular’], function (angular) {
‘use strict’;
return angular.module(‘app.ctlModule’, []);
});
声明一个叫作app.ctlModule的模块,app.js里的模块rabbit,依赖于这个模块,并调用这个模块下的所有“方法”

5、指令,服务,接口

同控制器的定义方法完全类似。

五、说明

php文件夹放了一些php文件,可以用apache建一个站点,模拟与服务器端的数据交互。

server.js是一个静态文件服务器,mac下进入目录,执行node server,即可创建一个简单的nodejs服务器。

个人感觉这样设计一个前端架构,虽然会让开发变得繁琐,但功能划分比较明确,提供了一个开发标准,适合于多人协同作业,总体上来说利大于弊。

源码下载地址:angularJS集成requireJS

时光飞逝 传奇谢幕

  上赛季,老将纷纷谢幕。皮尔洛远走大联盟,不知道当尤文赛季初打不开局面时,斑马球迷是否会怀念皮波后场远程制导般的长传…杰拉德泪洒安菲尔德,习惯了看到杰拉德带队进场的又岂止红军球迷….看到斯坦福桥的神灯穿上天蓝色球衣,蓝军球迷会否五味杂陈难以言说…

  本赛季结束,相似的一幕再次上演。罗西基和阿尔特塔与枪手告别,队长的泪水让枪迷动容。克洛泽告别蓝鹰,球迷久久不愿离场。美国老门将霍华德告别英超,阿比亚蒂告别米兰,迪纳塔莱告别乌迪内斯,托尼告别意甲,甚至连效忠了一世的狼王托蒂和蓝军特里都前途未卜…

  记忆里,还有曾经以为会永远飘扬在伯纳乌上空的皇马旗帜-指环王劳尔,还有大步流星阳光帅气的米兰卡卡,还有威震英伦霸气绝伦的魔兽德罗巴…

  记忆里,还有那支橙色的鲁能泰山,尤其是看到这赛季鲁能的举步维艰,更是让人想起当年那些驰骋沙场战功赫赫无敌猛将。中锋韩鹏头球无敌,小胖王永珀脚法细腻,摩托车吕征跑起来后面带土,还有李徽,队长刘金东,铁血后腰崔鹏,时不时来脚远射的周海滨,开球时习惯一看二退三磕脚的李雷雷,穿针引线的日科夫….

  不可否认,职业足球里,或许球员与俱乐部的合同是冰冷的,球员新陈代谢也是自然规律,但球员与球迷的感情却也是深厚的,球员对俱乐部的感情也是真挚的。伟大的俱乐部不正是靠这些伟大的球星传承积淀的精神与文化吗。总觉得还是那个红黑剑条衫22号的卡卡最快乐,还是那个白衣7号的劳尔最为潇洒…希望足坛多一些吉格斯和萨内蒂的轶事,少出现一些卡西泪别伯纳乌,神灯反戈蓝军的残酷。

  老将谢幕,新秀扬名。内马尔赛场如穿花蝴蝶过人如麻,阿扎尔左闪右晃似凌波微步,贝尔威风凛凛似追风逐电,J罗凌空抽射犹如石破天惊…看到后梅罗时代,身价动辄亿元的博格巴们精彩纷呈的表演固然赏心悦目,但感觉里总少了一份难以言表的激动,是因为少了当年宿舍里一起欢呼的伙伴?少了年少时的激情与冲动?蓦然回首,才知道了杰拉德们告别的不光是他们的职业生涯,还有留给我们的无数个美好瞬间,还有时光飞逝永远回不去的青春。

  老兵不死,传奇不朽。你们带给我们球迷的不只是美好瞬间,还有你们的精神,兢兢业业的精神,永不放弃的精神,追求荣誉的精神,忠心感恩的精神。我们会以你们为榜样,也会将这种精神传递给我们的下一代。祝老将们未来一切都好。

angularJS使用.jsonp方法

angularJS与服务器通信中的$http中有一个$http.jsonp方法,在网上偶然间看到一篇文章,对此解释得非常清楚。

原文地址:【AngularJs】 – JSONP跨域访问数据传输

json:数据存储格式.

jsonp:一种非官方的跨域数据交互协议.

AngularJS在http服务中的jsonp函数如下所示:

$http.jsonp("https://api.github.com?callback=JSON_CALLBACK").success(function(data) {
    //执行回调
})

1、当请求被发送时,AngularJS会在DOM中生成一个如下所示的<script>标签:

<script src="https://api.github.com?callback=angular.callbacks._0" type="text/javascript">

2、当支持JSONP的服务器返回数据时,数据会被包装在由AngularJS生成的具名函数angular.callbacks._0中。

3、在这个例子中,Github服务器响应如下:

angular.callbacks._0({ 
    'meta': {
        'X-RateLimit-Limit': '60', 
        'status': 200 
    },
    'data': {
        'current_user_url': 'https://api.github.com/user' 
    } 
}) 

4、angularJS中jsonp的使用

myUrl = "http://localhost:8090/api/test?callback=JSON_CALLBACK";

$http.jsonp(myUrl).success(
  function(data){
    alert(data);
  }
);

1.angularJS中使用$http.jsonp函数

2.指定callback和回调函数名,函数名为JSON_CALLBACK时,会调用success回调函数,JSON_CALLBACK必须全为大写。

3.也可以指定其它回调函数,但必须是定义在window下的全局函数。

4.url中必须加上callback

5.当callback为JSON_CALLBACK时,只会调用success,即使window中有JSON_CALLBACK函数,也不会调用该函数。

angularJS服务器通信之http

搜索angularJS通信时,看到一篇对$http通信解释得很是详细的文章,更难得的是文章的最后有一个对$http的封装,正好用在自己的angularJS项目的架构中。

原来地址:Angular服务器通信

参考文献:AngularJS权威教程

####一、快速上手

$http服务只接收一个配置对象作为参数,并且返回一个promise对象,两个回调方法success和error.

1、使用success()和error()

$http({
  method: 'GET',
  url: '/api/user/1'
}).success(function(rep) {
  // ...
}).error(function(err) {
  // ...
});

2、使用then()方法

$http({
  method: 'GET',
  url: '/api/user/1'
}).then(function(rep) {
  // 成功
}, function(err) {
  // 失败
});

3、服务器向客户端返回一个json数据,并带有一个状态码。

状态码约定为:

var rep_status = [
  {key: 'SUCCESS', value: 1, desc: '交互成功'},
  {key: 'NOT_LOGIN', value: 2, desc: '未登录'},
  {key: 'INVALID_REQUEST', value: 3, desc: '非法请求'},
  {key: 'INVALID_PARAM', value: 4, desc: '参数错误'},
  {key: 'INNER_ERROR', value: 5, desc: '服务器内部错误'},
  {key: 'UNKNOWN', value: 6, desc: '未知错误'}
]

返回API约定为:

// 响应成功返回
{
  status: 1,
  message: 'ok',
  data: {
    total: 200,
    object_list: [
      {
        // ....
      }
    ]
  }
}

// 出现错误后端返回
{
  status: 4,
  message: '参数错误'
}

4、通过success()和error()得到的响应数据只包含服务器响应数据的主体,如果想得到一个完整的对象,需要使用then()方法。一个完整的响应对象或者叫promise对象包含如下字段:

data - {string | Object} 响应主体 status - {number} 响应状态

headers - {function([headerName])} 获取响应头部的函数

config - {Object} 请求触发时提供的配置对象,后面会详细介绍

statusText - {string} 响应状态描述*

 var promise = $http({method: 'GET', url: '/api/user/5'});
promise.then(function(rep) {
  console.log(rep.data.data.name); // jenemy
  console.log(rep.status); // 200
  console.log(rep.statusText); // OK
  console.log(rep.config.url); // /api/user/5
  console.log(rep.headers()['x-powered-by']); // Express
});

5、$http配置属性

// params参数会转化为?name=jenemy&age=25

$http({
  method: 'GET',
  url: '/api/user/5',
  params: {
    name: 'jenemy',
    age: 25
  }
});

// data - {string | object} 在发送POST或PUT时使用

$http({
    method: 'POST',
    url: '/api/user/2',
    data: {name: "jenemy"},
    headers: {
        'Authorization': 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==',
        'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8'
    }
});

6、cache - {boolean | cache}

默认情况下,$http服务不会对请求进行本地缓存。启用缓存,需要向$http请求传入一个布尔值或者一个缓存实例。

$http.get('/api/user', { cache: true})
.success(function(data) {})
.error(function(data){});

当第一次请求成功后,$http会缓存请求的url,下一次请求时会直接加载缓存中的数据,不会再产生一次$http请求发出。

由于设置了启用缓存,AngularJS默认会使用$cacheFactory,这个服务是AngularJS在启动时自动创建的。

如果想要对AngularJS使用的缓存进行更多的自定义控制,可以请求传入一个自定义的缓存实例代替true。

例如,如果要使用LRU(Least Recently Used,最近最少使用)缓存,可以像这样传入缓存实例对象:

var lru = $cacheFactory('lru',{
    capacity: 20 //容纳
});
// $http请求
$http.get('/api/users.json', { cache: lru }) 
.success(function(data){}) 
.error(function(data){});    

现在,最新的20个请求会被缓存。第21个请求会导致lru从缓存中将时间比较老的请求移除。

通过.config()函数给所有$http请求设置一个默认的缓存:

angular.module('myApp', [])
    .config(function($httpProvider, $cacheFactory) {
        $httpProvider.defaults.cache = $cacheFactory('lru', {
             capacity: 20
        }); 
    });

7、拦截器

什么是拦截器?我们将httpProvider对象输出到控制台

{
  "defaults": {
    "transformResponse": [null],
    "transformRequest": [null],
    "headers": {
      "common": {
          "Accept": "application/json, text/plain, */*"
      },
      "post": {
          "Content-Type": "application/json;charset=utf-8"
      },
      "put": {
          "Content-Type": "application/json;charset=utf-8"
      },
      "patch": {
          "Content-Type": "application/json;charset=utf-8"
      }
    },
    "xsrfCookieName": "XSRF-TOKEN",
    "xsrfHeaderName": "X-XSRF-TOKEN",
    "paramSerializer": "$httpParamSerializer"
  },
  "interceptors": [],
  "$get": ["$httpBackend", "$$cookieReader", "$cacheFactory", "$rootScope", "$q", "$injector", null]
}

可以看到$httpProvider中有一个interceptors数组,而所谓拦截器也就是一个注入到了该数组中的服务。

var app = angular.module('app', []);

app.factory('myInterceptor', function($log) {
    $log.debug('$log输出啦。。。');

    var myInterceptor = {
      response: function(rep) {
        console.log(rep);
        return rep;
      }
    }
  });

app.config(function($httpProvider) {
  $httpProvider.interceptors.push('myInterceptor');
});

当我们在页面中向后端发起http请求时就可以看控制台中输出了我们的调试信息和服务器返回的数据。

关于拦截器的知识,见过的大都是理论上的解释。这里有一个关于angularJS拦截器应用实例的文章:angularjs中的interceptor和挺好的例子

####二、封装$http服务,统一管理请求url

将项目中所有的数据请求统一管理,无疑是一个非常好的开发体验,在以后开发中会借鉴这个思路,加入到自己的项目中。

在实际项目中,我们可以对$http服务作一层封装,比如封装成HttpService,将服务器返回的错误信息进行一次拦截处理,提供统一的错误提示。示例如下:

angular.module('httpService', [])
  .service('HttpService', function($http, $q) {

    var service = {
      get: function(url, config) {
        return handleRepData('get', url, null, config);
      },
      post: function(url, data, config) {
        return handleRepData('post', url, data, config);
      },
      put: function(url, data, config) {
        return handleRepData('post', url, data, config);
      },
      delete: function(url, config) {
        return handleRepData('delete', url, null, config);
      }
    }

    function handleRepData(method, url, data, config) {
      var promise;
      var defer = $q.defer();
      switch (method) {
        case 'get':
          promise = $http.get(url, config);
          break;
        case 'post':
          promise = $http.post(url, data, config);
          break;
        case 'put':
          promise = $http.put(url, data, config);
          break;
        case 'delete':
          promise = $http.delete(url, config)
      }

      promise.then(function(rep) {
        if (rep.data.success || rep.data.status === 1) {
          defer.resolve(rep.data);
        } else {
          var errorMsg = rep.data.message || '哦,出错啦!但是后端没有给任何信息。';
          // 弹出错误信息,或者重定向到404页面
          alert(errorMsg);
        }
      }, function() {
        defer.reject('出错了');
      })

      return defer.promise;
    }

    return service;
  });

将所有的请求url模块划分,统一放置在一个服务下面:

angular.module('urlService', [])
  .factory('UrlService', function(HttpService) {
    var service = {
      // 数据源
      cms: {
        dataSource: {
          // 新建
          new: function(params_) {
            return HttpService.post('/api/datasource/', params_).then(function(data_) {
              return data_;
            });
          },
          // 更新
          update: function(params_, name_) {
            return HttpService.put('/api/datasource/' + name_ +'/', params_).then(function(data_) {
              return data_;
            });
          },
          // 详情
          detail: function(params_, name_) {
            return HttpService.get('/api/datasource/'+ name_ +'/', {params: params_}).then(function(data_) {
              return data_;
            });
          },
          // 删除
          delete: function(params_, name_) {
            return HttpService.delete('/api/datasource/'+ name_ +'/', params_).then(function(data_) {
              return data_;
            });
          },
          {
            // ...
          }
        }
      },
      // 店铺管理
      store: {
        shop: {
          // 新建
          create: function(params_) {
            return HttpService.post('/api/shop/create/', params_).then(function(data_) {
              return data_;
            });
          },
          // 查重
          checkDuplicate: function(params_) {
            return HttpService.post('/api/shop/search_duplicate/', params_).then(function(data_) {
              return data_;
            });
          },
          // 搜索
          search: function(params_) {
            return HttpService.get('/api/shop/search/', params_).then(function(data_) {
              return data_;
            });
          },
          {
            // ...
          }
        }
      },
      {
        // ...
      }
    };

    return service;
  });

最后在控制器中引入urlService服务:

angular.module('cmsDataSourceController', [])
  .controller('cmsDataSourcePageCtrl', ['$scope', 'UrlService', function($scope, UrlService) {

    var params = {name: name, alias: alias, cate: cate, conds: conds, tags: tags};

    UrlService.cms.dataSource.new(params).then(function(rep) {
      tips('数据源创建成功!');
      $('.btn-update').text('修改数据源');
    });
  }]);

Restangular:专门用来与外界通信的angularjs库

####一、安装restangular

restangular依赖Lo-Dash或Underscore,需要引入这两个库中的任意一个。

<script type="text/javascript" src="restangular.js"></script>
<script type="text/javascript" src="lodash.js"></script>

通过Bower安装Restangular,Lo-Dash会同时被自动下载。

$ bower install restangular

将restangular当作依赖加载进应用模块。

var app = angular.module('app', ['restangular']);

完成后,就可以把Restangular服务注入到angularjs对象中。

app.factory('UserService', ['Restangular', function(Restangular) {
    //现在我们已经在UserService中访问了Restangular 
}])

####二、Restangular对象简介

Restangular有两种方式创建拉取数据的对象:

1、.all方法

var User = Restangular.all('users');
var allUsers = User.getList(); 

这样设置Restangular服务会让所有的HTTP请求将/users路径作为根路径来拉取数据,getList()方法会从 /users 拉取数据。

2、.one方法

var oneUser = Restangular.one('users', 'abc123');
oneUser.get().then(function(user) {
     user.getList('inboxes');
});

调用oneUser上的get()时向 /users/abc123/inboxes 发送请求。

####三、使用Restangular

当Restangular将初始化的对象返回给我们后,可以通过几种不同的方法与后端API进行交互。

1、getList() 获取信息

var message = Restangular.all('message');
var allMessages = message.getList();

2、post() 创建信息

var message = Restangular.all('message');
var newMessage = {
    body:'Hello World'
}
message.post(newMessage);

3、Restangular返回的是增强过的promise,因此除了可以调用then方法,还可以调用一些特殊的方法,比如$object。$object会立即返回一个空数组(或对象),在服务器返回信息后,数组会被新数据填充。 这对更新一个集合后,在作用域中立即重新获取集合时很有用:

message.post(newMessage).then(function(newMsg){
    $scope.message = message.getList().$object;
}, function(errorReason){
    //错误
})

4、remove() 发送一个delete http请求给后端

var message = message.get(123);
message.remove();

5、put() 支持对象的更新和存储

  • 要更新一个对象,首先要查询这个对象,然后在实例中设置新属性值,再调用对象的put()方法将更新保存到后端。

  • 在更新一个对象前对其进行复制,然后对复制的对象进行修改和保存。Restangular.copy()支持复制功能。

6、嵌套资源查询

var author = Restangular.one('authors','abc123');    //构建一个GET到/authors/abc123/books的请求
var books = author.getList('books');    

7、自定义函数 customMETHORD()

METHORD可以被以下方法替换:GET,GETLIST,DELETE,POST,PUT,HEAD,OPTIONS,PATCH,TRACE.

举例:

author.customGET('aaa');

auto.customPOST(
    {body: 'bbbbb'}, //post body
    'aaa', //路由
    {}, //自定义参数
    {}, //自定义头部
)    

####四、设置Restangular

1、设置baseUrl

给所有后端API请求设置baseUrl:

angular.module('myApp', ['restangular'])
     .config(function(RestangularProvider) {
         RestangularProvider.setBaseUrl('/api/v1');
     });

2、添加元素转换

elementTransformers 这个方法会在资源被加载后当做回调函数使用,在AngularJS对象中使用这些资源前,可以对其进行更新或修改。

angular.module('myApp', ['restangular'])
    .config(function(RestangularProvider) {    
         //3个参数:第一个路由、第二个不详、第三个回调
        RestangularProvider.addElementTransformer('authors', false, function(element) {
            element.fetchedAt = new Date();
            return element;
        });
});

扩展数据模型或集合

angular.module(‘myApp’, [‘restangular’])

.config(function(RestangularProvider) {   
     RestangularProvider.extendModel('authors', function(element) {
         element.getFullName = function() {
             return element.name + ' ' + element.lastName;
         };
         return element;
     });
});

3、响应拦截器 responseInterceptors

即为在响应从服务器返回时调用,调用时传入以下参数:

  • data:从服务器返回的数据
  • operation:使用的HTTP方法
  • what:所请求的数据模型
  • url:请求的相对URL
  • response:完整的服务器响应,包括响应头
  • deferred:请求的promise对象

下面的设置会使getList()返回一个带有元信息的数据。例如:向 /customers 发送get请求会返回一个像{customers: []}这样的数据

angular.module('myApp', ['restangular'])
.config(function(RestangularProvider) {
    RestangularProvider.setResponseInterceptor(function(data, operation, what) { 
        if (operation == 'getList') {
            var list = data[what];
            list.metadata = data.metadata;
            return list;
        }
        return data;
    });
});

4、请求拦截器 requestInterceptors

即为在向服务器发送请求之前进行操作。

tips:响应拦截器和请求拦截器应用小实例:我们可以同时使用他们来实现全页面范围内的加载提示。在每个请求之前开始加载提示,在收到请求后停止加载提示

传入以下参数:

  • element:发送给服务器的资源
  • operation:使用的HTTP方法
  • what:所请求的数据模型
  • url:请求的相对URL
1
2
3
4
5
6
7
8
9
10
angular.module('myApp', ['restangular'])
.config(function(RestangularProvider) {
RestangularProvider.setRequestInterceptor(function(elem, operation, what) {
if (operation === 'put') {
elem._id = undefined;
return elem;
}
return elem;
});
});

5、errorInterceptors 捕获错误

如果errorInterceptor返回false,promise链就会被中断,并且我们永远不需要处理错误。

例如,此时是处理验证失败的好时机,任何请求如果返回了401,可以通过errorInterceptor将其捕获并将用户重定向到登录页面。

angular.module('myApp', ['restangular'])
    .config(function(RestangularProvider) {
        RestangularProvider.setErrorInterceptor(function(resp) { 
            displayError();
            return false; //停止promise链  
        });
    });

五、建议将restangular封装在一个自定义的服务对象内。通过将restangular服务注入到自定义服务中,就可以方便的对restangular进行封装。在函数内部,使用withConfig()函数来创建自定义设置。

angular.module('myApp', ['restangular']).factory('MessageService', ['Restangular', function(Restangular) {
    var restAngular = Restangular.withConfig(function(Configurer) { 
            Configurer.setBaseUrl('/api/v2/messages');
    });
    var _messageService = restAngular.all('messages');
    return {
        getMessages: function() {
            return _messageService.getList();
        }
    }; 
}]);