2012-12-31 6 views
6

Ich versuche, ng-switch mit ng-include unten zu verwenden. Das Problem ist mit ng-init und der gesamte Controller-Block wird bei jeder ng-includes-Änderung neu gerendert.ng-include bewirkt, dass der Controller-Block wieder rendert

In der login_form.html, wenn ein Benutzer anmeldet, setze ich die IsLoggedIn = true, in der LoginCtrl. Dies führt jedoch dazu, dass der vollständige HTML-Code neu gerendert wird, wodurch der ng-init erneut ausgelöst wird.

Wie vermeide ich diesen Zyklus?

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

Unten ist der HTML-Code für das Login-Formular -

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

Im Folgenden wird der Controller -

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

}); 

Schlag der Service ist -

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

Antwort

38

Ich glaube, Ihr Problem ist ein Ergebnis der Art und Weise, wie prototypische Vererbung funktioniert. ng-include erstellt einen eigenen untergeordneten Bereich. Wenn Sie einen primitiven Wert in einem untergeordneten Bereich zuweisen, wird eine neue Eigenschaft in diesem Bereich erstellt, die die übergeordnete Eigenschaft schattiert/verbirgt.

Ich vermute, dass in login_form.html Sie so etwas wie die folgenden zu tun, wenn sich ein Benutzer anmeldet:

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

Bevor isLoggedIn auf true gesetzt ist, das ist, was Ihre Bereiche wie folgt aussehen:

before assignment

Nach isLoggedIn auf true gesetzt ist , das ist, was Ihre Bereiche wie folgt aussehen:

after assignment

Hoffentlich werden die Bilder deutlich zu machen, warum Das verursacht Ihnen Probleme.

Weitere Informationen darüber, warum prototypal Vererbung arbeitet auf diese Weise mit Primitiven finden Sie What are the nuances of scope prototypal/prototypical inheritance in AngularJS?

Wie der Link oben erklärt, Sie haben drei Lösungen:

  1. ein Objekt in der übergeordneten für Ihr Modell definieren Dann referenzieren Sie eine Eigenschaft dieses Objekts im Kind: parentObj.isLoggedIn
  2. Verwenden Sie $ parent.isLoggedIn in login_form.html - dies verweist dann auf das Primitiv im $ parent-Bereich, anstatt einen neuen zu erstellen. Zum Beispiel
    <a ng-click="$parent.isLoggedIn=true">login</a>
  3. Definieren Sie eine Funktion für den übergeordneten Bereich und rufen Sie sie vom untergeordneten Element ab - z. B. setIsLoggedIn(). Dadurch wird sichergestellt, dass die übergeordnete Bereichseigenschaft festgelegt wird und keine untergeordnete Bereichseigenschaft.

aktualisieren: Ihre HTML bei der Überprüfung, können Sie zwei Ebenen von untergeordneten Bereiche tatsächlich, da ng-Schalter und ng-umfassen jeweils ihre eigenen Bereiche erstellen. Also, die Bilder würden einen Enkelbereich benötigen, aber die drei Lösungen sind die gleichen ... außer für # 2, wo Sie $ parent verwenden müssten. $ Parent.isLoggedIn - hässlich. Deshalb schlage ich vor Option 1 oder 3

Update2: @ murtaza52 einige Code auf die Frage hinzugefügt ... Entfernen ng-init="isLoggedIn = false" von dem Controller (Ihr Dienst ist die Verwaltung der Login-Zustand über seine isUserLoggedIn Variable) und schalten Sie Login () in Ihrem Controller: <div ng-switch on="loginStatus()">.

Hier ist ein .

+0

Ich nehme an, meine Antwort ist Lösung 1? – asgoth

+0

@asgoth, nun, deine Geige benutzt im Wesentlichen die Lösung 1, obwohl es aufgrund der zusätzlichen Controller noch einen weiteren Kindbereich gibt. Ihre Antwort "Ein-Controller" verwendet im Wesentlichen Lösung 3 - Sie rufen eine Funktion im übergeordneten Bereich auf (die für untergeordnete Bereiche aufgrund einer prototypischen Vererbung verfügbar ist), um eine übergeordnete Bereichseigenschaft zu ändern. Aber dann gibt es auch ein bisschen Lösung 1, aufgrund der Eigenschaft des übergeordneten Objekts - Sie können ein primitiv mit Lösung 3 verwenden. Hier ist eine [Lösung für Lösung 3] (http://jsfiddle.net/mrajcok/jNxyE /) die ein Primitiv im übergeordneten Bereich verwendet. –

+0

+1 für die detaillierte Antwort Mark. Lassen Sie mich Ihre Lösung implementieren und sehen, ob sie das Problem löst. – murtaza52

3

Ich habe Habe eine working example. Der Trick besteht darin, dass die auszuwertende Gültigkeitsbereichsvariable ein Objekt und kein primitiver Typ sein muss. Es sieht so aus, als ob $scope.$watch() primitive Typen nicht richtig beobachtet (was ein Fehler ist). Das jsFiddle verfügt über einen übergeordneten Controller mit zwei untergeordneten Controllern, aber es funktioniert auch nur mit einem (übergeordneten) Controller.

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> 

Controller:

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

Hinweis: dies auch mit nur einem Controller funktioniert:

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

Dies führt auch zu dem gleichen Verhalten. Das erneute Rendern setzt die Variable immer noch zurück. Irgendwelche Ideen, wie man das erneute Rendern verhindern oder zurücksetzen kann? – murtaza52

+0

Nur sah, ging mein jfiddle in eine endlose Schleife :) – asgoth

+1

Redigiert meine Antwort. Überprüfen Sie das Beispiel im Link. – asgoth

0

Sie können den Status speichern, der die Reinitialisierung des Controllers in einem übergeordneten Bereich überstehen muss. Ich denke nicht, dass es seltsam ist, isLoggedIn sogar auf das $ rootScope zu setzen.

Sie könnten auch innerhalb des Controllers initialisieren, das wäre in diesem Fall sauberer (aber es löst Ihr Problem nicht).

Verwandte Themen