6

我正在尝试将 ng-switch 与下面的 ng-include 一起使用。问题在于 ng-init 和整个控制器块在任何 ng-includes 更改时重新渲染。

在 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@gmail.com": {name: "Robert Patterson", role: "Admin", email: "rob@gmail.com", password: "rob"},
            "steve@gmail.com":{name: "Steve Sheldon", role: "User", email: "steve@gmail.com", 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;
    }
 };
});
4

3 回答 3

38

我相信您的问题是原型继承工作方式的结果。ng-include 创建自己的子范围。在子作用域中分配原始值会在该作用域上创建一个新属性,该属性会隐藏/隐藏父属性。

我猜在 login_form.html 中,当用户登录时,您会执行以下操作:

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

在 isLoggedIn 设置为 true 之前,您的作用域如下所示:

分配前

isLoggedIn 设置为 true 后,您的作用域如下所示:

分配后

希望这些图片清楚地说明了为什么这会导致您出现问题。

有关为什么原型继承以这种方式与原语一起工作的更多信息,请参阅AngularJS 中范围原型/原型继承的细微差别是什么?

正如上面的链接所解释的,您有三个解决方案:

  1. 在模型的父对象中定义一个对象,然后在子对象中引用该对象的属性:parentObj.isLoggedIn
  2. 在 login_form.html 中使用 $parent.isLoggedIn - 然后这将引用 $parent 范围内的原语,而不是创建一个新的原语。例如,
    <a ng-click="$parent.isLoggedIn=true">login</a>
  3. 在父作用域上定义一个函数,并从子作用域调用它——例如,setIsLoggedIn()。这将确保正在设置父范围属性,而不是子范围属性。

更新:在审查您的 HTML 时,您实际上可能有两个级别的子范围,因为 ng-switch 和 ng-include 各自创建自己的范围。所以,图片需要一个孙范围,但三个解决方案是相同的......除了#2,你需要使用 $parent.$parent.isLoggedIn - 丑陋。所以我建议选项1或3。

Update2:@murtaza52 为问题添加了一些代码...ng-init="isLoggedIn = false"从您的控制器中删除(您的服务正在通过其isUserLoggedIn变量管理登录状态)并在您的控制器中打开 loginStatus() <div ng-switch on="loginStatus()">:。

这是一个工作小提琴

于 2012-12-31T17:20:32.797 回答
3

我有一个工作示例。诀窍是要评估的范围变量必须是对象,而不是原始类型。看起来$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;
    };
}
于 2012-12-31T11:44:38.557 回答
0

您可以将需要在控制器重新初始化后继续存在的状态存储在父作用域中。我认为将 isLoggedIn 放在 $rootScope 上并不奇怪。

此外,您可以在控制器内部进行初始化,在这种情况下会更干净(但它不能解决您的问题)。

于 2012-12-31T12:17:30.640 回答