Ожидание разрешения значения $rootScope в Angular перед загрузкой страницы

Итак, я столкнулся с этой проблемой, когда я использую ngView, и у меня есть статичная панель навигации, например:

<div ng-include="'views/nav.html'" ng-controller="NavCtrl"></div>
<div class="container-fluid" ng-view=""></div>

Этот nav.html, панель навигации, отображает определенный набор функций (Вход, Регистрация), если пользователь вышел из системы (используя ng-show), и другие параметры меню, если пользователь вошел в систему. Из-за интенсивного использования текущего user, я поместил эту информацию в $rootScope следующим образом: $rootScope.currentUser — возвращает объект пользователя, а $rootScope.signedIn — возвращает логическое значение.

По сути, я хочу отложить загрузку навигационной панели до тех пор, пока $rootScope.signedIn не будет загружено и либо истинно, либо ложно, а $rootScope.currentUser является объектом или неопределенным.

Я пытался возиться с созданием обещаний в моих маршрутах app.config, но я не уверен, как я могу вернуть обещание в постоянное состояние просмотра.

Любая помощь приветствуется.

Изменить:

Вот сервис, в котором я транслирую свой логин. Это срабатывает каждый раз, когда пользователь аутентифицируется/входит в систему или каждый раз, когда он выходит из системы:

    var authClient = new FirebaseSimpleLogin(refDownload, function(error, user) {
        if (error) {
            incorrectLogin(error.code);
        }
        if (user) {
            // user authenticated
            $rootScope.$broadcast('login');
            correctLogin(user.id);
        } else {
            // user is logged out
            $rootScope.$broadcast('logout');
        }
    });

Эта служба внедряется в контроллер NavCtrl следующим образом:

    $scope.isHidden = true;

    $scope.$on('login', function() {
        console.log('login broadcast');
        $scope.isHidden = false;
    });

    $scope.$on('logout', function() {
        console.log('broadcast logout');
        $scope.isHidden = true;
    });

Шаблон для этого контроллера — nav.html, который выглядит следующим образом:

<div class="col-xs-4 centered" id="nav-hover"  ng-show="isHidden">
    <ul class="nav navbar-nav">
        <li id="nav-login"><a ng-href="#/login"><span class="glyphicon glyphicon-log-in">&nbsp;Login</span></a></li>
    </ul>
</div>

<div class="col-xs-4 centered" id="nav-hover" ng-show="isHidden">
    <ul class="nav navbar-nav">
        <li id="nav-login"><a ng-href="#/register"><span class="glyphicon glyphicon-edit">&nbsp;Register</span></a></li>
    </ul>
</div>


<div class="col-xs-2 centered" id="nav-hover">
    <ul class="nav navbar-nav" ng-hide="isHidden">
        <li ng-class="{{ chatCat.active }}"><a ng-href="{{ chatCat.url }}"><span class="{{ chatCat.icon }}"></span></a></li>
    </ul>
</div>

Опять же, это представление привязано к NavCtrl. При входе пользователей я использую AuthCtrl следующим образом:

    $scope.login = function() {
        if ($scope.user !== undefined) {
            Auth.login($scope.user);
            $location.path('/dexter');
        } else {
            console.log('nothing entered');
        }               
    };

Когда я пытаюсь войти в систему, навигационное представление не обновляется с новыми значениями, хотя трансляция запускается из службы с «входом в систему».

Служба авторизации:

'use strict';

app.factory('Auth',
    function($rootScope, $location, $firebase, $firebaseSimpleLogin, firebaseUrl) {

    var refDownload = new Firebase(firebaseUrl + 'housemates');

    var sync = $firebase(refDownload); 

    var ref = sync.$asObject();

    var authClient = new FirebaseSimpleLogin(refDownload, function(error, user) {
        if (error) {
            incorrectLogin(error.code);
        }
        if (user) {
            // 1
            // user authenticated
            correctLogin(user.id);
        } else {
            // user is logged out
            // $rootScope.signedIn = false;
        }
    });

    var Auth = {

        housemates: ref,

        changeColor: function(color) {
            var id = $rootScope.currentUser.id.toString();
            refDownload.child(id).update({ color: color });
            $rootScope.currentUser.color = color;
        },


        create: function(authUser, usr) {
            refDownload.child(authUser.id).set({
                initials: usr.initials,
                email: authUser.email,
                password: usr.password,
                color: 'Blue',
                id: authUser.id,
                uid: authUser.uid,
                rememberMe: true,
            });

        },

        // 3
        findById: function(id) {
            refDownload.on('value', function(snapshot) {
                var userObject = snapshot.val();
                // 4 - sets currentUser
                //$rootScope.currentUser = userObject[id];
                var currentUser = userObject[id];
                Auth.setUser(currentUser);
                // $rootScope.signedIn = true;
            }, function (error) {
                console.log(error);
            });
        },

        login: function(user) {
            authClient.login('password', {
                email: user.email,
                password: user.password,
                rememberMe: true
            });
        },

        logout: function() {
            delete $rootScope.currentUser;
            delete $rootScope.signedIn;
            delete $rootScope.error;
            return authClient.logout();
        },

        register: function(user) {
            var userSimple = user;
            authClient.createUser(user.email, user.password, function(error, user) {
                if(!error) {
                    var userComplex = user;
                    Auth.login(userSimple);
                    Auth.create(userComplex, userSimple);
                    return user;
                } else {
                    console.log(error);
                }
            });

        },

        setUser: function(aUser) {
            console.log('setuser ran');
            $rootScope.currentUser = aUser;
            console.log('setUser: ' + $rootScope.currentUser);
        },

        isLoggedIn: function() {
            console.log($rootScope.currentUser);
            return ($rootScope.currentUser) ? $rootScope.currentUser : false;
        },


    };

    // 2
    function correctLogin(id) {
        Auth.findById(id);
    }

    function incorrectLogin(error) {
        alert(error);
        $rootScope.error = error;
    }

    return Auth;


});

person Himmel    schedule 03.10.2014    source источник
comment
Возможно, вам поможет ngCloak. Или $timeout, может быть...   -  person Denis C de Azevedo    schedule 03.10.2014
comment
Я попытался поместить ng-cloak в верхний div в представлении nav.html, но это не решает проблему.   -  person Himmel    schedule 03.10.2014
comment
Вам также следует попробовать службу вместо $rootScope.   -  person Sergiu Paraschiv    schedule 03.10.2014
comment
Я начал использовать службу для своих текущих сведений о пользователе, но я вызываю информацию о своем пользовательском объекте буквально на каждой странице и в представлениях - разве это не вариант использования для $ rootScope? Кроме того, это не решает мою проблему.   -  person Himmel    schedule 03.10.2014
comment
Для $rootScope нет вариантов использования, за исключением, может быть, широковещательных передач, и вам также следует избегать их. Служба является постоянной (что-то вроде синглтона), поэтому в $scope.foo = someService.getSomeData(); нет ничего плохого.   -  person Sergiu Paraschiv    schedule 03.10.2014
comment
Кроме того, чтобы решить проблему, только вы знаете, что загружает $rootScope.signedIn. В этом коде вы также можете $rootScope.userLoaded = false; __load the data__.onSuccessDo: $rootScope.userLoaded = true;, а затем ng-show="userLoaded" на панели навигации.   -  person Sergiu Paraschiv    schedule 03.10.2014
comment
Спасибо за подсказку, я буду работать над преобразованием своих объектов $rootScope в служебные объекты, из которых они были получены. Есть ли у вас какие-либо советы о моем нынешнем затруднительном положении?   -  person Himmel    schedule 03.10.2014
comment
@Himmel прочитайте документы для ng-cloak, чтобы он работал правильно во всех случаях, вам нужно сделать некоторые вещи с CSS, чтобы элемент, с которым вы его используете, мог быть скрыт как можно скорее при загрузке страницы. Другой подход — взглянуть на Angular UI Router — это как ngView на стероидах. У него есть хорошая функция разрешения, которая не будет отображать представление, пока ваше обещание не будет разрешено.   -  person Sunil D.    schedule 03.10.2014
comment
@SergiuParaschiv Мой $rootScope.signedIn функционально возвращает значение true или false. Итак, я запускаю ng-show=signedIn, на мой взгляд. Моя проблема в том, что $rootScope.signedIn является ложным или неопределенным, пока служба не загрузит данные. Таким образом, он кратко показывает панель навигации с ложным значением ng-show, пока пользователь не щелкнет что-то.   -  person Himmel    schedule 03.10.2014
comment
@СунилД. Я рассмотрю UI Router для будущего использования, но, поскольку в настоящее время я использую ngView, я пока не собираюсь делать капитальный ремонт.   -  person Himmel    schedule 03.10.2014
comment
На первый взгляд все в порядке (хотя я никогда не использовал firebase), но, чтобы быть уверенным, пожалуйста, поместите {{isHidden}} где-нибудь в nav.html и включите консоль разработчика Chrome, чтобы увидеть, не нарушила ли какая-то ошибка код javascript.   -  person zszep    schedule 03.10.2014
comment
Я поставил {{ isHidden }} в Nav, и вход в систему, похоже, не меняет значение. Однако, когда я щелкаю элемент на панели навигации, значение isHidden обновляется. Мне кажется проблема в контроллере. В моем index.html я вызываю навигационный вид и навигационный Ctrl. Но когда открывается страница входа, она использует AuthCtrl, а не NavCtrl. Может ли это быть проблемой? Могу ли я обернуть свои div в index.html вокруг ng-include? Как я могу изменить значение isHidden в области навигации от других контроллеров? Разве он не должен автоматически меняться при входе/выходе из службы?   -  person Himmel    schedule 03.10.2014


Ответы (3)


С небольшим количеством $rootScope.$broadcast и ng-hide в меню этого можно легко добиться. См. этот плункер

HTML:

<!DOCTYPE html>
<html ng-app="plunker">

  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <link rel="stylesheet" href="style.css" />
    <script data-require="[email protected]" src="https://code.angularjs.org/1.2.25/angular.js" data-semver="1.2.25"></script>
    <script src="app.js"></script>
  </head>

  <body ng-controller="MainCtrl">
  <div ng-include="'nav.html'" ng-controller="NavCtrl" ng-hide="isHidden"></div>
  <button class="btn" ng-click="login()">Login</button>
  <button class="btn" ng-click="logout()">Logout</button>
  </body>

</html>

Яваскрипт:

var app = angular.module('plunker', []);

app.controller('MainCtrl', function($scope, $rootScope) {
  $scope.login = function() {
    $rootScope.$broadcast("login");
  }

  $scope.logout = function() {
    $rootScope.$broadcast("logout");
  }
});

app.controller('NavCtrl', function($scope) {
  $scope.isHidden = true;
  $scope.$on('login', function() {
    console.log("logged in");
    $scope.isHidden = false;
  });

  $scope.$on('logout', function() {
    console.log("logged out");
    $scope.isHidden = true;
  });
});
person zszep    schedule 03.10.2014
comment
Я успешно передал событие входа/выхода из системы на контроллер NavCtrl, однако при успешном входе пользователя панель навигации не меняется, пока пользователь не щелкнет что-то. - person Himmel; 03.10.2014
comment
Что вы имеете в виду под панелью навигации, которая не меняется, пока пользователь что-то не щелкнет? Насколько я понял ваш вопрос, вы хотите показать или скрыть панель навигации в соответствии со статусом входа пользователя. - person zszep; 03.10.2014
comment
Кажется, что трансляция регистрируется, потому что NavCtrl console.logs вошел в систему, но представление по какой-то причине не обновляется, когда пользователь входит в систему ... пользователь должен щелкнуть что-то, прежде чем вид навигационной панели изменится - person Himmel; 03.10.2014
comment
Вы пробовали мой образец плунжера? Это то поведение, которое вы хотите? - person zszep; 03.10.2014
comment
Это то поведение, которое я хотел. Однако трансляция изменяет вид/настройку состояния только при загрузке страницы, а не когда пользователь входит в систему как мгновенное изменение. - person Himmel; 03.10.2014
comment
Это означает, что панель навигации отражает изменения или получает состояние, когда я загружаю приложение правильно, и трансляция срабатывает, но когда я вхожу в систему, трансляция срабатывает, но состояние не меняется. - person Himmel; 03.10.2014
comment
Я не знаю, как именно вы обрабатываете процесс входа в систему, но он должен следовать коду в примере, который я написал. Когда пользователь аутентифицирован, транслируется событие входа в систему, а в контроллере, где родительское представление панели навигации имеет событие on, которое ожидает отправки события входа в систему. Затем установите для переменной (hidden) значение false и подключите ее к директиве ng-hide в теге navbar. - person zszep; 03.10.2014
comment
Ваш пример был очень полезен, я реализовал его, и он работал, как и ожидалось. При аутентификации пользователя служба передает «вход в систему», область действия подхватывает это и регистрирует «вход в систему» ​​на консоли. Таким образом, каждый раз, когда пользователь аутентифицируется, или выходит из системы, или входит в систему, или что-то еще, широковещательное событие срабатывает, как и ожидалось. Но когда я вхожу в систему вручную, представление не обновляется с помощью «isHidden». Он регистрируется только после того, как пользователь щелкнет что-то еще. - person Himmel; 03.10.2014
comment
Может быть, вы можете предоставить пример кода. Я уверен, что это поможет. Я думаю, у вас проблема с синхронизацией. Возможно, вы выполняете аутентификацию в серверной службе через http-вызов, и это занимает некоторое время, поэтому вам нужно использовать промисы, чтобы узнать, когда вызов завершен. - person zszep; 03.10.2014
comment
Пожалуйста, смотрите обновление выше, я предоставил примеры службы, которая получает данные, $broadcast в службе, контроллер, в который они вводятся, и представление. Я также показал контроллер, связанный с представлением шаблона входа. - person Himmel; 03.10.2014

Хорошо, если способ, который я предложил, не работает для вас, вот второе возможное решение (plunker< /а>)

Основная идея состоит в том, чтобы иметь службу (в данном случае фабрику), в которой вы устанавливаете имя пользователя, вошедшего в систему, а затем в навигационном контроллере используете $watch для просмотра изменений статуса аутентификации в службе. И код:

<!DOCTYPE html>
<html ng-app="plunker">

  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <link rel="stylesheet" href="style.css" />
    <script data-require="[email protected]" src="https://code.angularjs.org/1.2.25/angular.js" data-semver="1.2.25"></script>
    <script src="app.js"></script>
    <script src="Auth.js"></script>
  </head>

  <body ng-controller="MainCtrl">
  <div ng-include="'nav.html'" ng-controller="NavCtrl" ng-hide="isHidden"></div>
  <button class="btn" ng-click="login()">Login</button>
  <button class="btn" ng-click="logout()">Logout</button>
  </body>

</html>

Яваскрипт:

var app = angular.module('plunker', []);

app.controller('MainCtrl', function($scope, $rootScope, Auth) {
  $scope.login = function() {
    var user = "iris"
    Auth.setUser(user);
  }

  $scope.logout = function() {
    Auth.setUser(null);
  }
});

app.controller('NavCtrl', function($scope, Auth) {
  $scope.isHidden = true;

  $scope.$watch(Auth.isLoggedIn, function (value, oldValue) {

    console.log("authentication changed");

    if(!value && oldValue) {
      console.log("logged out");
      $scope.isHidden = true;
    }

    if(value) {
      console.log("logged in");
      $scope.isHidden = false;
    }

  }, true);


});

и обслуживание:

app.factory('Auth', function() {
  var user;

  return {
    setUser: function(aUser) {
      user = aUser;
    },
    isLoggedIn: function() {
      console.log(user);
      return (user) ? user : false;
    }
  }
})
person zszep    schedule 03.10.2014
comment
Хм, я попытался добавить функцию isLoggedIn вместе с контроллером $watch, но моя служба аутентификации сложнее, чем немедленная установка пользователя в качестве текущего пользователя. Он должен пройти аутентификацию, а затем найти пользователя по его идентификационному номеру. Выложу всю фабрику выше. - person Himmel; 04.10.2014
comment
Ну, я подозревал, что в вашем коде аутентификации есть некоторая сложность, которая мешает коду работать. Если у вас есть еще один зависимый шаг (например, поиск пользователя по идентификатору), вам нужно использовать обещания. В любом случае, я рад, что вы решили свою проблему. - person zszep; 04.10.2014
comment
Спасибо за вашу помощь. - person Himmel; 04.10.2014

Ответ @zszep $broadcast решил проблему с одной оговоркой. Необходимо было добавить $scope.$apply() после каждой из команд $scope.isHidden в NavCtrl. Это вызвало своего рода обновление страницы и обновление представления Nav.

person Himmel    schedule 03.10.2014