angular-按需加载angularjs和$http.post的二三事

前面的24个章节都是关于yii2框架的理解以及应用,本来是向按照一个一个章节系列写下去的,每个分类都能够比较集中,清晰。但是在项目中又会用到其他的知识,只能够按照平时积累写下去咯,可能会有些乱!

最近前端比较火的框架,可数facebook的react了,一个专门针对移动端开发的前端高性能框架。当然google的angularjs也是一个很火的前端框架,在SPA应用上算是非常出色的。具体的基础入门大家可以看看菜鸟网络的http://www.runoob.com/angularjs/angularjs-tutorial.html,这里都是一些基本知识点

下面要说的是,我本人在应用angular中遇到的一些问题和平时用jquery实现的方式的异同。
(1)单页面应用中加载有关angualr的js文件
在进入单页面的初始化时,所有用到的angualr的js需要一次性加载,不能做到按需加载。
比如在路由中,我们设置了很多的不同页面和对应路由,那么进入不同的路由,我们就需要按需加载不同的的angualr的controller的js文件了。如果我们在页面中直接通过<script src="./test.js"></script>加载angular的这个test.js文件时,这个只会通过xhr的异步对象请求方式加载进来的,test.js文件时加载进来了,但是它并没有将view视图中双向绑定那样,把请求返回来的数据通过$scope的作用域绑定显示出来。具体的原因是因为通过xhr异步加载的test.js文件已将是处于angular的上下文之外了,说的简单点,即是处于两个不同的环境域中,所以对于数据绑定也就是model来说,这个过程是跨域的,不会发生的。(AngularJS 上下文之外的任何地方修改了 model ,那么你就需要通过手动调用 $apply() 来通知 AngularJS 。这就像告诉 AngularJS ,你修改了一些 models ,希望 AngularJS 帮你触发 watchers 来做出正确的响应)。当然如果是普通的js文件,这是可以加载并且可以执行的。

那么对于这种情况,我们应该怎么解决呢?因为在单页应用中,当功能越来越多,越来越复杂的时候,我们就需要按需来加载不同路由下的js文件了!下面,我在github上找到一个比较好的开源的解决方案!
官网:https://oclazyload.readme.io/docs/oclazyloadprovider
ui-router的演示:http://plnkr.co/edit/u18KQc?p=preview

那么我们可以npm install oclazyload 安装

var app = angular.module('app', ['ngRoute','oc.lazyLoad']);
app.config(['$routeProvider',
			function($routeProvider) {
				$routeProvider
					.when('/main', {
						templateUrl: '/home/index/main',
						controller: 'MainCtrl'
					})
					.when('/dashboard', {
						templateUrl: '/home/index/view',
						controller: 'DashboardCtrl',
			            resolve: {
				             loadMyCtrl:['$ocLazyLoad',function ($ocLazyLoad) {
				                 return $ocLazyLoad.load({
				                    name:"view",
				                    files:["/static/js/angular-gridster/demo/dashboard/script.js"]
				                 });
				             }]
				         }
					})
					.otherwise({
						redirectTo: '/main'
					});
		}
])
其中最重要就是

resolve: {
         loadMyCtrl:['$ocLazyLoad',function ($ocLazyLoad) {
             return $ocLazyLoad.load({
                name:"view",
                files:["/static/js/angular-gridster/demo/dashboard/script.js"]
             });
         }]
     }
这个resove 是在执行路由渲染模板前进行一个操作,resove里面的值由key:value组成,key的值可以自定义,基本模式就是这样子,return 回来一个promise.

所以按照上面的路由配置就可以方便实现按需动态加载,可以给我们的开发带来更加清晰的过程。

angular本身的ng-router在功能上还是受限,建议如果要用路由的话,可以用开源的angular-ui-router,这个功能强大,而且可以多个层级的路由,模板嵌套等。
安装angular-ui-router 直接用npm install angular-ui-router


(2)angular的$http.post(),传输json数据格式问题

在实际项目中,我们遇到了一个很常见问题,当使用
$http.post(url,{'name':'bing','sex':1}).success(function(response){

});
post一个json数据过去的时候,在服务端php(或者一些其他语言),当我们用$_POST的时候,却没有办法获取到这个json数据。


因为按照我们平时在用jquery的时候
$.post({
url:...,
data:{'name':'bing','sex':1},
.....
});
同样也是这样子,一般是可以成功的将数据post到服务端的,为什么angualr就不可以呢?

在httpd通信的请求中,发送请求时,可以通过content-type指定请求体的发包是以何种格式来实现的!

application/x-www-form-urlencoded是标准的表单发包方式,普通的表单提交,或者js发包,默认都是通过这种方式实现的。也就是说post方法的请求体内容数据最终必须转成application/x-www-form-urlencoded,也就是'name=bing&sex=1',服务端才可以通过$_POST获取到数据。

那么jquery为什么可以呢?

下面看一下jquery的官方说明:
data:将自动转换为请求字符串格式。GET 请求中将附加在 URL 后面。查看 processData 选项说明,以禁止此自动转换。对象必须为"{键:值}"格式。如果这个参数是一个数组,jQuery会按照traditional 参数的值, 将自动转化为一个同名的多值查询字符串(查看下面的说明)。注:如 {foo:["bar1", "bar2"]} 转换为 'foo=bar1&foo=bar2'。
也就是说jquery会自动将json数据格式转为字符串格式后(序列化后),再进行post的传输发包。

而在angualr中,请求post过去的json数据并不会转为name=bing&sex=1这种格式的字符串,而是默认就是json,但由于http通信中并没有所谓的json,所有在传输的时候,也就是,application/json可以将它理解为text/plain,普通字符串。那么不满足发包的格式。自然在服务端$_POST就没法接收数据了。

具体的解决办法:
(1)利用jquery的$.param()方法

var queryData = $.param({'name':'bing','sex':1});

 $http({
      url : 'test.php',
      method : 'POST',
      data : queryData,
      headers : {'Content-Type':'application/x-www-form-urlencoded'},
      }).success(function(response) {

      });
其中headers : {'Content-Type':'application/x-www-form-urlencoded'}需要设置。
有人说,缺点就是引入jquery,但本人认为在一个应用系统中,大部分都会使用jquery的,angular和jquery相互结合开发,才是强强组合。

而且$.param()处理对象序列化的层数更深层,这个也是非常方便。

(2)利用angular自身封装的$httpParamSerializer()

  • {'foo': 'bar'} results in foo=bar
  • {'foo': Date.now()} results in foo=2015-04-01T09%3A50%3A49.262Z (toISOString() and encoded representation of a Date object)
  • {'foo': ['bar', 'baz']} results in foo=bar&foo=baz (repeated key for each array element)
  • {'foo': {'bar':'baz'}} results in foo=%7B%22bar%22%3A%22baz%22%7D (stringified and encoded representation of an object)
另外还可以使用 $httpParamSerializerJQLike

https://docs.angularjs.org/api/ng/service/$httpParamSerializerJQLike

一种使用方法

$http({
  url: myUrl,
  method: 'GET',
  params: myParams,
  paramSerializer: '$httpParamSerializerJQLike'
});
另一种使用方法

.controller(function($http, $httpParamSerializerJQLike) {
  //...

  $http({
    url: myUrl,
    method: 'POST',
    data: $httpParamSerializerJQLike(myData),
    headers: {
      'Content-Type': 'application/x-www-form-urlencoded'
    }
  });

});

(3)自己重新定义序列化

var ballApp = angular.module('BallApp', [], function($httpProvider) {
    // 修改 header
    $httpProvider.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';

    // Serialize function
    //只能是嵌套两层的对象
    var param = function(obj) {
        var query = '', name, value, fullSubName, subName, subValue, innerObj,i;
        for(name in obj) {
            value = obj[name];
            //如果值是数组
            if(value instanceof Array) {
                for(i = 0; i < value.length; ++i) {
                    subValue = value[i];
                    fullSubName = name + '[' + i + ']';
                    innerObj = {};
                    innerObj[fullSubName] = subValue;
                    query += param(innerObj) + '&';
                }
            //如果值是对象
            } else if(value instanceof Object) {
                for(subName in value) {
                    subValue = value[subName];
                    fullSubName = name + '[' + subName + ']';
                    innerObj = {};
                    innerObj[fullSubName] = subValue;
                    query += param(innerObj) + '&';
                }
            如果值是字符串
            } else if(value !== undefined && value !== null) {
                query += encodeURIComponent(name) + '=' + encodeURIComponent(value) + '&';
            }
        }

        return query.length ? query.substr(0, query.length - 1) : query;
    }

    //重写transformRequest参数处理方法(利用param function 处理)
    $httpProvider.defaults.transformRequest = [function(data) {
        return angular.isObject(data) && String(data) !== '[object File]' ? param(data) : data;
    }];
});

ballApp.controller('AjaxCtrl', function($scope, $http) {
    $scope.ajax = function() {
        $http.post('test.php', {name: 'Febr'})
        .success(function(data) {
            console.log(data);
        });
    };
});


如果您觉得本文对你有帮助,那就按按鼠标,顶一下,让更多的人可以看到它......






  • 9
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值