使用AngularJS构建应用时遇到的问题及解决方案(版本为1.3.9)

最近在公司使用用AngularJS(1.3.9)完成了一个项目,在此记录一下过程当中遇到的问题及解决方案。html

使用$http服务发送ajax请求时后端没法判断请求是XMLHttpRequest

问题及场景
有时候后端会读取请求中headerX-Requested-With字段判断前端的请求是否为异步请求XMLHttpRequest,在使用$http服务发送请求时后端却判断为false前端

缘由
'X-Requested-With' : 'XMLHttpRequest'并不属于标准的header内容,所以Angular不会在header中默认设置该字段。ajax

解决方案
手动在$httpProvider中设置该字段,代码以下:segmentfault

$httpProvider.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';

note:
能够建立一个公用服务,在配置方法作这个修改,公用服务注入到每一个module里就一劳永逸了。后端

使用$http.post()方法参数类型不正确

问题及场景
在angular中,使用$http.post()方法提交数据时,发现所带参数并不是Form Data,而是JSON对象,致使服务器没法使用通常方法正确获取参数,而使用jQuery的$.post()方法却能够正确获取。安全

缘由
二者的post对header的处理有所不一样,jQuery会把做为JSON对象的myData序列化,例如:服务器

var myData = { a : 1, b : 2 };
    // jQuery在post数据以前会把myData转换成字符串:"a=1&b=2"

angular显然没作这个处理。app

解决方案
修改Angular的$httpProvider的默认处理,代码以下:异步

$httpProvider.defaults.transformRequest = function(obj){
        var str = [];
        for(var p in obj) {
            str.push(encodeURIComponent(p) + '=' + encodeURIComponent(obj[p]));
        }
        return str.join("&");
    };

    $httpProvider.defaults.headers.post = {
        'Content-Type': 'application/x-www-form-urlencoded'
    };

note:
一样应该在公用服务中作此修改。ide

调用$scope.$apply(fn)更新视图时报错$rootScope:inprog

问题及场景
在更新$scope上的model数据时,若是是在digest监听外,会发现视图并无自动更新,因而手动调用$scope.$apply(fn)方法通知视图进行更新,却发现有时候会报错$rootScope:inprog

缘由
该错误缘由是在进程当中$scope.$apply(fn)正在执行,不能在此基础上重复调用该方法。

解决方案
能够在调用该方法时作一个安全检测,封装代码以下:

$scope.safeApply = function(fn) {
        var phase = this.$root.$$phase;
        if (phase === '$apply' || phase === '$digest') {
            if (fn && (typeof(fn) === 'function')) {
                fn();
            }
        } else {
            this.$apply(fn);
        }
    };

note:
$$phase变量是scope中的一个内部属性,若是为null或者undefined则说明进程中没有$apply方法在运行,则能够直接调用,不然直接执行入参方法。

添加统一拦截器对ajax的请求或返回作处理

问题及场景
我遇到的场景是在发送异步请求时,后端会先判断用户是否已经登陆,若是未登陆则会在responseheader中添加一个redirecturl字段,前端读取该字段控制页面跳转到该url,我首先想到的解决方案就是前端须要作一个统一拦截器,对全部异步请求的response进行拦截。

解决方案
经过看Angular中$httpProvider部分的源码注释,发现了拦截器的几种写法,我选择的写法源码注释以下:

*   // register the interceptor as a service
    *   $provide.factory('myHttpInterceptor', function($q, dependency1, dependency2) {
    *     return {
    *       // optional method
    *       'request': function(config) {
    *         // do something on success
    *         return config;
    *       },
    *
    *       // optional method
    *      'requestError': function(rejection) {
    *         // do something on error
    *         if (canRecover(rejection)) {
    *           return responseOrNewPromise
    *         }
    *         return $q.reject(rejection);
    *       },
    *       // optional method
    *       'response': function(response) {
    *         // do something on success
    *         return response;
    *       },
    *
    *       // optional method
    *      'responseError': function(rejection) {
    *         // do something on error
    *         if (canRecover(rejection)) {
    *           return responseOrNewPromise
    *         }
    *         return $q.reject(rejection);
    *       }
    *     };
    *   });
    *   $httpProvider.interceptors.push('myHttpInterceptor');

我拦截response的代码以下:

commonService.factory('redirectInterceptor', function(){
        return {
            'response': function(response) {
                if(response.headers().redirecturl) {
                    window.location.href = response.headers().redirecturl;
                }
                return response;
            }
        };
    });

    $httpProvider.interceptors.push('redirectInterceptor');

note
最后一行表示将该拦截器登记到$httpProvider的拦截器中,一样应该写在公用服务的配置方法当中。

controller之间的通讯问题

问题及场景
当有两个视图分别由两个controller控制时,其中一个视图发生变化,需通知另外一个视图产生了此变化。

解决方案
总的来讲,Angular中控制器通讯有三种处理方法:

  • 利用做用域继承的方式 即子控制器继承父控制器中的内容;

  • 基于事件的方式 即$on,$emit,$boardcast这三种方法;

  • 服务方式 写一个服务的单例而后经过注入来使用。

我选择了最后一种方法,示例代码以下:

//JS
    var app = angular.module('myApp', []);
    app.factory('instance', function(){
        return {};
    });
    app.controller('MainCtrl', function($scope, instance) {
        $scope.change = function() {
        instance.name = $scope.test;
        };
    });
    app.controller('sideCtrl', function($scope, instance) {
        $scope.add = function() {
            $scope.name = instance.name;
        };
    });
//html
    <div ng-controller="MainCtrl">
        <input type="text" ng-model="test" />
        <div ng-click="change()">click me</div>
    </div>
    <div ng-controller="sideCtrl">
        <div ng-click="add()">my name </div>
    </div>

note:
在Angular中服务是一个单例,因此在服务中生成一个对象,该对象就能够利用依赖注入的方式在全部的控制器中共享。
若是不是经过点击产生变化,还可结合$scope.$watch()方法来进行通讯。
其余两种方法可参考站内文章:AngularJS控制器controller如何通讯?

结语

以上为我在编写一个angular应用时遇到的问题及解决方案,记录并分享出来,欢迎你们指正!

相关文章
相关标签/搜索