使用AngularJS構建應用時遇到的問題及解決方案(版本為1.3.9)

jopen 8年前發布 | 10K 次閱讀 Web框架 angularjs

最近在公司使用用AngularJS(1.3.9)完成了一個項目,在此記錄一下過程中遇到的問題及解決方案。

使用 $http 服務發送ajax請求時后端無法判斷請求是 XMLHttpRequest

問題及場景:

有時候后端會讀取請求中 header 的 X-Requested-With 字段判斷前端的請求是否為異步請求 XMLHttpRequest ,在使用 $http 服務發送請求時后端卻判斷為 false 。

原因:

'X-Requested-With' : 'XMLHttpRequest' 并不屬于標準的 header 內容,因此Angular不會在 header 中默認設置該字段。

解決方案:

手動在 $httpProvider 中設置該字段,代碼如下:

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

note:

可以創建一個公用服務,在配置方法做這個修改,公用服務注入到每個module里就一勞永逸了。

</div>

使用 $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顯然沒做這個處理。

解決方案:

修改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'
};</pre> 

note:

同樣應該在公用服務中做此修改。

調用 $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 方法在運行,則可以直接調用,否則直接執行入參方法。

</div>

添加統一攔截器對ajax的請求或返回做處理

問題及場景:

我遇到的場景是在發送異步請求時,后端會先判斷用戶是否已經登錄,如果未登錄則會在 response 的 header 中添加一個 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');</pre> 

我攔截 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');</pre> 

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如何通信?

</div>

結語

以上為我在編寫一個angular應用時遇到的問題及解決方案,記錄并分享出來,歡迎大家指正!

來自: http://hao.jser.com/archive/9129/

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!