ng-include заставляет блок контроллера перерисовываться

Я пытаюсь использовать ng-switch с ng-include ниже. Проблема в ng-init, и весь блок контроллера перерисовывается при любом изменении ng-include.

В файле login_form.html, когда пользователь входит в систему, я устанавливаю isLoggedIn = true в LoginCtrl. Однако это вызывает повторную визуализацию полного 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;
    }
 };
});

3 ответа

Решение

Я считаю, что ваша проблема - результат того, как работает наследование прототипов. ng-include создает свою собственную дочернюю область. Присвоение примитивного значения в дочерней области создает новое свойство в этой области, которое скрывает / скрывает родительское свойство.

Я предполагаю, что в login_form.html вы делаете что-то вроде следующего, когда пользователь входит в систему:

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

Перед тем как isLoggedIn установлен в true, ваши области видимости выглядят так:

до назначения

После того, как для isLoggedIn установлено значение true, ваши области видимости выглядят так:

после назначения

Надеемся, что фотографии ясно показывают, почему это вызывает у вас проблемы.

Для получения дополнительной информации о том, почему наследование прототипов работает таким образом с примитивами, см. Каковы нюансы области видимости прототипа / прототипа наследования в AngularJS?

Как объясняет приведенная выше ссылка, у вас есть три решения:

  1. определите объект в родительском для вашей модели, а затем укажите свойство этого объекта в дочернем: parentObj.isLoggedIn
  2. используйте $parent.isLoggedIn в login_form.html - это будет ссылаться на примитив в области $ parent, а не создавать новый. Например,
    <a ng-click="$parent.isLoggedIn=true">login</a>
  3. определите функцию в родительской области и вызовите ее из дочернего элемента - например, setIsLoggedIn(). Это обеспечит установку родительского свойства области, а не дочернего свойства области.

Обновление: при просмотре вашего HTML у вас могут быть два уровня дочерних областей, так как каждый из ng-switch и ng-include создает свои собственные области. Таким образом, для картинок потребуется область внука, но три решения одинаковы... за исключением #2, где вам нужно будет использовать $parent.$ Parent.isLoggedIn - безобразно. Поэтому я предлагаю вариант 1 или 3.

Обновление 2: @ murtaza52 добавил код к вопросу... Удалить ng-init="isLoggedIn = false" с вашего контроллера (ваш сервис управляет состоянием входа через isUserLoggedIn переменная) и включите loginStatus() в вашем контроллере: <div ng-switch on="loginStatus()">,

Вот рабочая скрипка.

У меня есть рабочий пример. Хитрость в том, что оцениваемая переменная области должна быть объектом, а не примитивным типом. Это выглядит как $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;
    };
}

Вы можете сохранить состояние, необходимое для повторной инициализации контроллера, в родительской области. Я не думаю, что странно даже помещать isLoggedIn в $rootScope.

Кроме того, вы можете инициализировать внутри контроллера, который в этом случае будет чище (но это не решит вашу проблему).

Другие вопросы по тегам