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 = {"[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;
}
};
});
Ответы
Ответ 1
Я считаю, что ваша проблема связана с тем, как работает прототипное наследование. ng-include создает свою собственную дочернюю область. Присвоение примитивного значения в дочерней области создает новое свойство в этой области, которое скрывает/скрывает родительское свойство.
Я предполагаю, что в login_form.html вы делаете что-то вроде следующего, когда пользователь входит в систему:
<a ng-click="isLoggedIn=true">login</a>
До того, как isLoggedIn установлено значение true, это выглядит так:
После того, как значение isLoggedIn установлено в true, это выглядит так:
Надеемся, что на фотографиях будет понятно, почему это вызывает проблемы.
Для получения дополнительной информации о том, почему прототипное наследование работает таким образом с примитивами, см. Каковы нюансы объема прототипа/прототипического наследования в AngularJS?
Как видно из приведенной выше ссылки, у вас есть три решения:
- определить объект в родительском для вашей модели, а затем ссылаться на свойство этого объекта в child: parentObj.isLoggedIn
- используйте $parent.isLoggedIn в login_form.html - это будет ссылаться на примитив в $parent scope, а не на создание нового. НАПРИМЕР,
<a ng-click="$parent.isLoggedIn=true">login</a>
- определить функцию в родительской области и вызвать ее из дочернего элемента - например, 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()">
.
Вот рабочая скрипка.
Ответ 2
У меня есть рабочий рабочий пример. Фокус в том, что переменная области видимости должна быть объектом, а не примитивным типом. Похоже, что $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;
};
}
Ответ 3
Вы можете сохранить состояние, которое должно пережить повторную инициализацию контроллера в родительской области. Я не думаю, что это странно поставить isLoggedIn на $rootScope даже.
Кроме того, вы можете инициализировать внутри контроллера, что в этом случае будет более чистым (но это не решит вашу проблему).