div with ng-show not updating after ajax call
Asked Answered
H

2

11

Very new to angular; I'm sure I'm missing something really obvious.

I have a login form on my page. What I want to happen is to have the form hide after the user successfully logs in.

My entire page uses a single controller.

Here's the HTML; I've stripped out all but the login fields, although there is actually other content inside the same controller:

<div ng-app="Localizer" ng-controller="StoreListCtrl" ng-init="my_occasion = ''">
    <div ng-show="user_signed_in===false">

        <div class="row"><h1>Have an account?</h1>
            <p>Sign in to quickly access addresses saved to your account</p></div>
            <div class="row">
                <div class="data">
                    <input type="username" ng-model="username" name="username" data-placeholder="Username" />
                </div>
                <div class="data">
                    <input type="password" ng-model="password" name="password" data-placeholder="Password" />
                </div>
                <div class="data"> 
                    <div class="syo-acctSignBtn yButtonTemplate" ng-click="login_user()">Sign In<div class="btnArrow iconPosition"></div></div>
                </div>
        </div>
    </div>
</div>

When the user clicks the Sign In button, a function called login_user() runs. This works; if I manually refresh the page, I do see that I'm logged in and the div that needs to be hidden is hidden. But without refreshing the page, the div remains visible, even though the variable it relies on (user_signed_in) does get updated.

Here's the login_user function in my controller; I've stripped out a lot of stuff, including front-end field validation:

$scope.login_user = function() {
    $.post('/site/ajax/login/do_login', {'username': $scope.username, 'password': $scope.password}, function(data) {
        if (data.success) {
                    $window.user_state.status = $window.user_status_key.STATE_SIGNED_IN;
            $scope.user_signed_in = $window.user_state.status == $window.user_status_key.STATE_SIGNED_IN;
            console.log("user status: " + $window.user_state.status );
                    console.log("user status key: " + $window.user_status_key.STATE_SIGNED_IN);
                    console.log("scope.user_signed_in: " + $scope.user_signed_in);
        } else {
            Modal({
                title: "Could not Login",
                text: data['errMsg'],
                yellow_button: {btn_text: "OK"}
            });
        }
    });

};

This is the result of the console.log lines:

user status: signed in user status key: signed in scope.user_signed_in: true

Since user_signed_in does not equal false, I would expect all the form content shown inside the <div ng-show="user_signed_in===false"> div to hide. But it doesn't (again, unless I manually refresh the page.) What am I missing?

Hydrotherapeutics answered 13/3, 2014 at 20:16 Comment(2)
That should work, you could try using ng-show="!user_signed_in". Could you set up a plunker or jsfiddle showing the problem.Expanded
Have you tried ng-hide = "user_signed_in" ? If you're sure that everything in your code is correct, then there is a chance that angular hasn't finished its digest cycle. try using $scope.$apply() just before writing to console.Optician
G
23

The problem is that you update the variable (user_logged_in) outside of the Angular context, so Angular knows nothing about it (until you refresh the page for example).

You should wrap the body of your callback in $scope.$apply():

$apply() is used to execute an expression in angular from outside of the angular framework. (For example from browser DOM events, setTimeout, XHR or third party libraries). Because we are calling into the angular framework we need to perform proper scope life cycle of exception handling, executing watches.

(emphasis mine)


BTW, you wouldn't face the same problem if you performed your request using the $http service, because it is Angular-context-aware.

Gravimeter answered 13/3, 2014 at 20:30 Comment(4)
Like I said, I'm very new at angular, so please forgive me if this is a stupid question. I don't know what you mean when you say that I update the variable outside of the angular context. It's being updated inside the $scope.login_user function, and the variable is called $scope.user_signed_in. How is that outside the angular context?Hydrotherapeutics
OK, I still don't understand why it's out of context in my original version, so would appreciate an explanation. However, I changed it over to using the $http service and it works. So thank you very much.Hydrotherapeutics
I don't want to ruin it for you, but the Angular magic isn't so...magic. The difference when you make change inside the Angular context (e.g. in a controller or using an Angulsr service) is that Angular takes care of the $appky stuff transparently. Basically, you need to look into Angular's digest cycle and understand what $apply(), $digest() and $watch() do (I find this article to be a good starting point and there are plenty of great and detsiled answers here on SO.Gravimeter
Regarding your question "why is it outside of the Angular context": The assignment to scope vatiables is not dobe in the controller (as you seem to believe). Instead a callback function is registered with jQuery's post method. Then the controller function completes executing. As soon as jQuery gets a response it updates the scope variables, but at the time Angular knows nothing about it (so we have to explicitely initiate a digest cycle so that Angular gets up-to-date with any changes in the model).Gravimeter
C
0
function ShowLoading($scope) {
$scope.loading = true;
setTimeout(function () {
   $scope.$apply(function(){
            $scope.loading = false;
   });
 }, 2000);
}
Castorina answered 16/2, 2017 at 12:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.