2013-05-03 26 views
9

我是新來的角,我有一個艱難的時間來到這個問題的底部。Angular - 綁定到返回承諾的函數

我在寫一個單頁面應用程序,並且正在處理驗證部分。我有一個名爲「sessionService」的服務,我希望能夠在整個應用程序中使用它來確定用戶是否已登錄。這很簡單,如果我做這樣的事情:

...service('sessionService', function(...) { 
    /*...snip...*/ 
    this.isLoggedIn = function() { 
     return this.authenticated; 
    }; 
}); 

其中「身份驗證」只是服務的私人。但是,如果我刷新頁面,則會崩潰。所以,我想到的是做這樣的事情:

/*...snip...*/ 
this.isLoggedIn = function() { 
    var deferred = $q.defer() 
    , self  = this 
    ; 

    function handleLoggedInStatus(status) { 
    if (status) { 
     self.authenticated = true; 
     deferred.resolve(); 
    } 
    else { 
     deferred.reject(); 
    } 
    } 

    if (this.authenticated === null) { 
    $http.get('/user') 
     .success(function(response) { 
     handleLoggedInStatus(response.success); 
     }); 
    } 
    else { 
    handleLoggedInStatus(this.authenticated); 
    } 

    return deferred.promise; 
}; 

然後在我的控制,我會做這樣的事情:

$scope.isLoggedIn = sessionService.isLoggedIn; 

而且在我的模板,我會做:

...data-ng-show="isLoggedIn()" 

但是,這樣做會導致以下錯誤:

10 $digest() iterations reached. Aborting! 

我想引用sessionService.isLoggedIn功能的幾個不同的方法,如:

$scope.isLoggedIn = sessionService.isLoggedIn(); 
$scope.isLoggedIn = sessionService.isLoggedIn.bind(sessionService)(); 
$scope.isLoggedIn = function() { return sessionService.isLoggedIn() } 

但他們要麼沒有工作,或者只是給了我同樣的錯誤。

基本上,我只是希望能夠返回一個承諾,告訴我用戶是否登錄。如果我們不知道他們是否登錄(如刷新頁面後),承諾將在ajax請求後解決。如果我們已經知道(比如在單頁面應用中使用正常導航),那麼承諾將立即解決。然後我想在我的視圖中使用它,這樣我可以顯示/隱藏某些內容,例如註銷或查看帳戶頁面的鏈接。

我在做什麼錯?

回答

9

您正在解決您的承諾,但沒有解決您的承諾 - 因此$scope解決方案的承諾價值爲undefined,這是虛假的,因此您的ng-show未觸發。

看來你正在尋找的東西更多的是這樣的:

在服務:

function handleLoggedInStatus(status) { 
    if (status) { 
    self.authenticated = true; 
    } 
    deferred.resolve(status); // always resolve, even if with falsy value 
} 

if (this.authenticated === null) { 
    $http.get('/user') 
    .success(function(response) { 
     handleLoggedInStatus(response.success); 
    }) 
    .error(function(data) { 
     deferred.reject(data.errorMsg); // reject if there was an error 
    }); 
} else { 
    handleLoggedInStatus(this.authenticated); 
} 

在控制器:

$scope.loggedIn = sessionService.isLoggedIn(); 

在HTML:

<div ng-show='loggedIn'>...</div> 

Here is a JSFiddle演示如何解析延遲的真實值並綁定到$scope


請注意,您不能在功能本身綁定到範圍

$scope.loggedIn = sessionService.isLoggedIn 

並調用該函數在視圖

<div ng-show="loggedIn()">...</div> 

,因爲該函數返回一個不同承諾每個摘要週期(這就是爲什麼你得到'10摘要週期'錯誤)。但是,您可以確保對sessionService.isLoggedIn的額外呼叫返回相同的承諾,而不是創建新的呼叫,因爲您可以多次呼叫then(實際上這是承諾的好處之一):

deferred = null; 

isLoggedIn: function() { 
    if (!deferred) { 
    deferred = $q.defer(); 
    $http.get('/user') 
     .success(function(response) { 
     deferred.resolve(response.success); // resolve if true or false 
     }) 
     .error(function(data) { 
     deferred.reject(data.errorMsg); // reject if there was an error 
     }); 
    } 
    return deferred.promise; 
} 

然後,您可以擺脫this.authenticated布爾值,因爲您不需要跟蹤函數調用中以前登錄的用戶(因爲承諾會爲您執行此操作)。

但是,雖然這擺脫了摘要循環錯誤,但您仍然無法從視圖調用函數 - 我懷疑Angular將返回值(承諾本身)視爲真值,而不是綁定到承諾的解決價值。 Here's an example of it not working;注意即使該承諾正在使用false解決,也會顯示div


要使用deferred.reject指示用戶正在認證,在原來的服務,你會想更多像這樣的做一些控制器,雖然我相信resolve荷蘭國際集團與false更清潔:

sessionService.isLoggedIn() 
    .then(function() { 
    $scope.loggedIn = true; // resolved 
    }).then(function() { 
    $scope.loggedIn = false; // rejected 
    }); 
+0

謝謝!除了一件事以外,它的工作很棒:當sessionService登錄或註銷時,$ scope.isLoggedIn不會自動更新。如果我刷新它的頁面,但不會在那之前。這是$ scope的情況。$ watch?或者有其他解決方法嗎? – Scott 2013-05-03 21:28:41

+0

是的,你是對的。如果狀態能夠在應用程序的整個過程中進行更改,我可能根本不會使用承諾 - 相反,我可能會將'sessionService'綁定到作用域,並在我的視圖中綁定到類似'sessionService.authenticated'的東西。另一個不錯的選擇是在登錄狀態更改時從'$ rootScope'](http://docs.angularjs.org/api/ng.$ro​​otScope.Scope#$broadcast)'[$ broadcast]一個事件,並綁定在必要時舉辦活動。 – 2013-05-03 22:04:31

+0

當然,您可以隨時回到最初的想法(始終產生一個新的承諾),並且必須確保在必要時使用「true」或「false」來解決它。 – 2013-05-03 22:05:31