2012-12-31 16 views
6

Próbuję użyć przełącznika ng z ng-include poniżej. Problem polega na tym, że ng-init i cały blok kontrolera są ponownie renderowane na każdej zmianie zawierającej ng.ng-include powoduje, że blok kontrolera ponownie wyświetla się

W login_form.html, kiedy użytkownik się loguje, ustawiamy isLoggedIn = true w LoginCtrl. Powoduje to jednak ponowne renderowanie pełnego html poniżej, co powoduje ponowne uruchomienie ng-init.

Jak uniknąć tego cyklu?

 <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> 

Poniżej znajduje się kod HTML do formularza logowania -

<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> 

Poniżej kontroler -

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(); 
    }; 

}); 

Cios jest usługa -

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; 
    } 
}; 
}); 

Odpowiedz

38

Wierzę, że twój problem wynika ze sposobu działania prototypowego dziedziczenia. ng-include tworzy własny zakres podrzędny. Przypisanie prymitywnej wartości w zakresie podrzędnym tworzy nową właściwość tego zasięgu, która cienie/ukrywa właściwość nadrzędną.

Zgaduję, że w login_form.html zrobić coś jak poniżej, gdy użytkownik loguje się:

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

Przed isLoggedIn jest ustawiony na true, to co twoi zakresy wyglądać następująco:

before assignment

Po isLoggedIn jest ustawiony na true jest to, co się zakresy wyglądać następująco:

after assignment

Mam nadzieję, że zdjęcia sprawiają, że jasne, dlaczego to powoduje problemy.

Aby uzyskać więcej informacji o tym, dlaczego prototypal dziedziczenia działa w ten sposób z prymitywów, proszę zobaczyć What are the nuances of scope prototypal/prototypical inheritance in AngularJS?

Ponieważ powyższy link wyjaśnia, masz trzy rozwiązania:

  1. definiujących obiekt w dominującej dla danego modelu , a następnie odwołaj się do właściwości tego obiektu w podrzędnym: parentObj.isLoggedIn
  2. użyj $ parent.isLoggedIn w login_form.html - to odwoła się do prymitywu w zakresie $ parent, a nie do nowego. Np.,
    <a ng-click="$parent.isLoggedIn=true">login</a>
  3. zdefiniować funkcję w zakresie nadrzędnym i wywołać ją z elementu podrzędnego - np. SetIsLoggedIn(). Zapewni to ustawienie właściwości obiektu nadrzędnego, a nie podrzędnej właściwości zakresu.

Aktualizacja: w przeglądzie kodu HTML, można rzeczywiście mieć dwa poziomy zakresów podrzędnych, ponieważ NG-switch i ng obejmują każdy tworzyć własne zakresy. Tak więc, zdjęcia będą wymagać zakresu wnuka, ale trzy rozwiązania są takie same ... z wyjątkiem # 2, gdzie trzeba użyć $ parent. $ Parent.isLoggedIn - brzydko. Proponuję więc opcja 1 lub 3.

Update2: @ murtaza52 dodany jakiś kod na pytanie ... Usuń ng-init="isLoggedIn = false" z kontrolera (usługa zarządza stan logowania za pośrednictwem swojej zmiennej isUserLoggedIn) i włączyć LoginStatus () w kontrolerze: <div ng-switch on="loginStatus()">. To jest .

+0

Przypuszczam, że moja odpowiedź jest rozwiązanie 1? – asgoth

+0

@gasgoth, cóż, twoje skrzypce używają zasadniczo rozwiązania 1, chociaż istnieje jeszcze inny zakres podrzędny ze względu na dodatkowe kontrolery. Odpowiedź "jeden kontroler" polega zasadniczo na rozwiązaniu 3 - wywołujesz funkcję w zakresie nadrzędnym (która jest dostępna dla zakresów podrzędnych z powodu prototypowego dziedziczenia), aby zmienić właściwość zakresu nadrzędnego. Ale jest tam też trochę rozwiązania 1, ze względu na właściwość obiektu nadrzędnego - możesz użyć prymitywu z rozwiązaniem 3. Oto [skrzypce dla rozwiązania 3] (http://jsfiddle.net/mrajcok/jNxyE /), który używa prymitywu w zakresie nadrzędnym. –

+0

+1 za szczegółową odpowiedź Oznacz. Pozwól mi zaimplementować Twoje rozwiązanie i sprawdź, czy rozwiązuje problem. – murtaza52

3

mam mieć working example. Sztuczka polega na tym, że zmienna zasięgu do oceny musi być obiektem, a nie typem pierwotnym. Wygląda na to, że $scope.$watch() nie obserwuje prawidłowo typów pierwotnych (co jest błędem). JsFiddle ma kontroler nadrzędny z dwoma kontrolerami podrzędnymi, ale działałby również tylko z jednym kontrolerem nadrzędnym.

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> 

Kontroler:

function LoginCheckCtrl($scope) { 
    $scope.user = { 
     login: false 
    }; 
} 

Uwaga: ten będzie również pracować tylko z jednym kontrolerem:

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; 
    }; 
} 
+0

Powoduje to również takie samo zachowanie. Ponowne renderowanie nadal resetuje zmienną. Jakieś pomysły dotyczące zapobiegania ponownemu uruchamianiu lub resetowaniu? – murtaza52

+0

Właśnie zobaczyłem, moje jsfiddle poszło do niekończącej się pętli :) – asgoth

+1

Zmieniono moją odpowiedź. Sprawdź przykład w linku. – asgoth

0

Można zapisać stan, który musi przetrwać ponowną inicjalizację kontrolera w zakresie nadrzędnym. Nie wydaje mi się dziwne umieszczanie isLoggedIn na $ rootScope nawet.

Można również zainicjować wewnątrz kontrolera, który w tym przypadku byłby czystszy (ale nie rozwiązuje problemu).

Powiązane problemy