Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
632 views
in Technique[技术] by (71.8m points)

angularjs - ng-include causes the controller block to re render

I am trying to use ng-switch with ng-include below. The problem is with ng-init and the whole controller block getting re-rendered on any ng-includes change.

In the login_form.html, when a user logins, I set the isLoggedIn = true, in the LoginCtrl. However this causes the re-rendering of the full html below, which causes the ng-init again.

How do I avoid this cycle?

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

Below is the HTML for the login form -

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

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

});

Blow is the service -

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;
    }
 };
});
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

I believe your problem is a result of the way prototypal inheritance works. ng-include creates its own child scope. Assigning a primitive value in a child scope creates a new property on that scope that shadows/hides the parent property.

I'm guessing that in login_form.html you do something like the following when a user logs in:

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

Before isLoggedIn is set to true, this is what your scopes look like:

before assignment

After isLoggedIn is set to true, this is what your scopes look like:

after assignment

Hopefully the pictures make it clear why this is causing you problems.

For more information about why prototypal inheritance works this way with primitives, please see What are the nuances of scope prototypal / prototypical inheritance in AngularJS?

As the above link explains, you have three solutions:

  1. define an object in the parent for your model, then reference a property of that object in the child: parentObj.isLoggedIn
  2. use $parent.isLoggedIn in login_form.html -- this will then reference the primitive in the $parent scope, rather than create a new one. E.g.,
    <a ng-click="$parent.isLoggedIn=true">login</a>
  3. define a function on the parent scope, and call it from the child -- e.g., setIsLoggedIn(). This will ensure the parent scope property is being set, not a child scope property.

Update: in reviewing your HTML, you may actually have two levels of child scopes, since ng-switch and ng-include each create their own scopes. So, the pictures would need a grandchild scope, but the three solutions are the same... except for #2, where you would need to use $parent.$parent.isLoggedIn -- ugly. So I suggest option 1 or 3.

Update2: @murtaza52 added some code to the question... Remove ng-init="isLoggedIn = false" from your controller (your service is managing the login state via its isUserLoggedIn variable) and switch on loginStatus() in your controller: <div ng-switch on="loginStatus()">.

Here is a working fiddle.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...