Изчаква се стойността на $rootScope да се разреши в Angular преди зареждане на страницата

Така че се натъквам на този проблем, когато използвам ngView и имам навигационна лента, която е статична навсякъде по следния начин:

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

Този nav.html, лентата за навигация, показва определен набор от функции (Влизане, Регистриране), ако потребителят е излязъл (чрез ng-show) и други опции на менюто, ако потребителят е влязъл. Поради интензивното използване на текущия потребител, поставих тази информация в $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="/bg#/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="/bg#/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="/bg{{ 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 е false или недефиниран, докато услугата не зареди данните. Така че за кратко показва навигационната лента с фалшивата стойност ng-show, докато потребителят щракне върху нещо.   -  person Himmel    schedule 03.10.2014
comment
@SunilD. Ще разгледам 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 в nav scope от други контролери? Не трябва ли да се променя автоматично с влизанията/излизанията от услугата?   -  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="/bg' + document.location + '" />');</script>
    <link rel="stylesheet" href="/bgstyle.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>

JavaScript:

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
Не знам как точно управлявате процеса на влизане, но той трябва да следва кода в примера, който написах. Когато потребителят е удостоверен, излъчете събитие за влизане и в контролера, където е родителският изглед на навигационната лента, има събитие за включване, което чака събитието за влизане да бъде изпратено. След това задайте променлива (скрита) на 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="/bg' + document.location + '" />');</script>
    <link rel="stylesheet" href="/bgstyle.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>

JavaScript:

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
Е, подозирах, че има някаква сложност във вашия код за удостоверяване, която пречи на кода да работи. Ако имате друга зависима стъпка (като намиране на потребител по id), трябва да използвате обещания. Както и да е, радвам се, че си решил проблема. - 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