2012-12-31 32 views
6

我想在下面用ng-include使用ng-switch。問題在於ng-init和整個控制器塊在任何ng-includes變更中重新呈現。ng-include導致控制器塊重新渲染

在login_form.html中,當用戶登錄時,我在LoginCtrl中設置了isLoggedIn = true。但是這會導致下面的完整html重新呈現,這又會導致ng-init。

我該如何避免這個循環?

 <div ng-controller="LoginCtrl" ng-init="isLoggedIn = false" class="span4 pull-right"> 
     <div ng-switch on="isLoggedIn"> 
      <div ng-switch-when="false" ng-include src="'login_form.html'"></div> 
      <div ng-switch-when="true" ng-include src="'profile_links.html'"></div> 
     </div> 
     </div> 

下面是登錄表單的HTML -

<form class="form-inline"> 
    <input type="text" placeholder="Email" ng-model="userEmail" class="input-small"/> 
    <input type="password" placeholder="Password" ng-model="userPassword" class="input-small"/> 
    <button type="submit" ng-click="login(userEmail, userPassword)" class="btn">Sign In</button> 
</form> 

下面是控制器 -

angularApp.controller('LoginCtrl', function($scope, currentUser){ 

    $scope.loginStatus = function(){ 
    return currentUser.isLoggedIn(); 
    }; 

/* $scope.$on('login', function(event, args) { 
    $scope.userName = args.name; 
    }); 

    $scope.$on('logout', function(event, args) { 
    $scope.isLoggedIn = false; 
    });*/ 

    $scope.login = function(email, password){ 
    currentUser.login(email, password); 
    }; 

    $scope.logout = function(){ 
    currentUser.logout(); 
    }; 

}); 

吹的是服務 -

angularApp.factory('currentUser', function($rootScope) { 
    // Service logic 
    // ... 
    // 
    var allUsers = {"rob[email protected]": {name: "Robert Patterson", role: "Admin", email: "[email protected]", password: "rob"}, 
      "[email protected]":{name: "Steve Sheldon", role: "User", email: "[email protected]", password: "steve"}} 

    var isUserLoggedIn = false; 

    // Public API here 
    return { 
    login: function(email, password){ 
     var user = allUsers[email]; 
     var storedPass = user.password; 

     if(storedPass === password){ 
     isUserLoggedIn = true; 
     return true; 
     } 
     else 
     { 
     return false; 
     } 
    }, 
    logout: function(){ 
     $rootScope.$broadcast('logout'); 
     isUserLoggedIn = false; 
    }, 

    isLoggedIn: function(){ 
     return isUserLoggedIn; 
    } 
}; 
}); 

回答

38

我相信你的問題是原型繼承工作方式的結果。 ng-include創建它自己的子範圍。在子作用域中分配一個原始值會在該作用域上創建一個新的屬性,用於隱藏/隱藏父屬性。

我猜在login_form。HTML中你做類似下面的當用戶登錄:

<a ng-click="isLoggedIn=true">login</a> 

之前isLoggedIn設置爲true,這是你的範圍是什麼樣子:

before assignment

isLoggedIn後設置爲true ,這是你的範圍是什麼樣子:

after assignment

希望的照片說清楚爲什麼這是造成你的問題。

有關爲何原型繼承這種方式工作與原語的更多信息,請參閱What are the nuances of scope prototypal/prototypical inheritance in AngularJS?

正如上面的鏈接解釋說,有三種解決方案:

  1. 定義父的對象模型,然後引用子對象中的該屬性:parentObj.isLoggedIn
  2. 在login_form.html中使用$ parent.isLoggedIn - 然後它將引用$ parent作用域中的原語,而不是創建一個新的。例如,
    <a ng-click="$parent.isLoggedIn=true">login</a>
  3. 在父範圍上定義一個函數,並從子項調用它 - 例如setIsLoggedIn()。這將確保設置父級範圍屬性,而不是子範圍屬性。

更新:在審查你的HTML,你實際上可能有子作用域的兩個級別,因爲NG-開關和NG-包括每個創建自己的作用域。所以,這些圖片需要一個孫子範圍,但是這三種解決方案是一樣的...除了#2,你需要使用$ parent。$ parent.isLoggedIn - 醜陋。因此,我建議選擇1或3

UPDATE2:@ murtaza52添加一些代碼的問題......從你的控制器中刪除ng-init="isLoggedIn = false"(您的服務管理通過其isUserLoggedIn變量登錄狀態)和loginStatus開關( )在您的控制器中:<div ng-switch on="loginStatus()">

這裏是一個。

+0

我想我的答案是解決方案1? – asgoth

+0

@asgoth,好吧,你的小提琴基本上使用瞭解決方案1,但由於附加的控制器還有另一個子範圍。你的「一個控制器」答案基本上使用瞭解決方案3 - 你正在調用父範圍中的函數(由於原型繼承,子範圍可用)來更改父範圍屬性。但是由於父對象的屬性,在那裏也有一些解決方案1 ​​ - 您可以在解決方案3中使用基元。下面是[解決方案3的小提琴](http://jsfiddle.net/mrajcok/jNxyE /)在父範圍中使用一個基元。 –

+0

+1爲詳細答案標記。讓我實施您的解決方案,看看它是否能解決問題。 – murtaza52

3

我已經有working example。訣竅是要評估的範圍變量必須是一個對象,而不是一個基本類型。它看起來像$scope.$watch()沒有正確觀察原始類型(這是一個錯誤)。 jsFiddle有一個帶兩個子控制器的父控制器,但它也可以只與一個(父)控制器一起工作。

HTML:

<div ng-controller="LoginCheckCtrl"> 
     <div ng-switch on="user.login"> 
      <div ng-switch-when="false" ng-include="'login'"></div> 
      <div ng-switch-when="true" ng-include="'logout'"></div> 
     </div> 
</div> 

控制器:

function LoginCheckCtrl($scope) { 
    $scope.user = { 
     login: false 
    }; 
} 

注:這也將只有一個控制器工作:

function LoginCheckCtrl($scope) { 
    $scope.user = { 
     login: false 
    }; 
    $scope.login = function() { 
     console.log($scope.user.login ? 'logged in' : 'not logged in'); 
     $scope.user.login = true; 
    }; 
    $scope.logout = function() { 
     console.log($scope.user.login ? 'logged in' : 'not logged in'); 
     $scope.user.login = false; 
    }; 
} 
+0

這也會導致相同的行爲。重新渲染仍會重置變量。任何想法如何防止重新渲染或重置? – murtaza52

+0

剛纔看到,我的jsfiddle進入了一個無限循環:) – asgoth

+1

編輯我的答案。檢查鏈接中的示例。 – asgoth

0

您可以存儲需要在父範圍內存活控制器重新初始化的狀態。我認爲把$ rootScope中的isLoggedIn放在甚至是不奇怪的。此外,您可以初始化控制器內部,在這種情況下,這將更清潔(但它不能解決您的問題)。