使用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應用時遇到的問題及解決方案,記錄並分享出來,歡迎你們指正!

相關文章
相關標籤/搜索