2012-12-31 29 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放在甚至是不奇怪的。此外,您可以初始化控制器内部,在这种情况下,这将更清洁(但它不能解决您的问题)。