3

I am needing to do the following activities from my HTML page:

  1. User enters email and password to register
  2. Form is sent to Controller when user clicks Submit
  3. Control uses AJAX to create JSON request to RESTful Server and the server responds accordingly.
  4. According to Server's response, User should get alert window, and a message printed on the current page (register.html) below "Submit". If it is a "Success - Registered " or "Failed - Could not register".

But, currently it is working like this: 1), 2), 3) working as expected.

  1. The user gets the alert window on first clicking Submit with the appropriate message.

  2. The user gets the message printed ONLY ON CLICKING Submit AGAIN, but once again, this is showing the alert window.

If I remove alert('Something') from JS, I have to click twice for the message to print on the register.html

Also, I want to bring it to your attention that clicking twice is also making server call twice. It behaves as if it has paused after the server call, and then to print the message I am supposed to click on Submit.

register.html

<div class='container'>
    <div class='col-md-12'>
        <div class='fill' ng-controller="registerController">
            <form name="userForm" ng-submit="submitForm(userForm.$valid,user)" novalidate>
                <!-- ALL FORM ELEMENTS HERE -->
                <button type="submit" class="btn btn-primary" ng-disabled="userForm.$invalid" ng-click="showme=true">
                    Submit
                </button>
                <div class="wrapper">
                    <h3 ng-show="showme"><em>{{msgalert}}</em></h3>
                </div>
            </form>
        </div>
    </div>
</div>

My JS Controller looks like this stackoverflow_q3.js

// create angular controller
var register = angular.module('Main-app');
register.controller('registerController', function ($scope, $http, $location) {

    // function to submit the form after all validation has occurred            
    $scope.submitForm = function (isValid, user) {
        console.log('Stackoverflow JS func caled');
        var regData = {
            "email": user.email,
            "password": user.password1
        };

        var jsonData = JSON.stringify(regData);
        var request = $.ajax({
            url: 'myurl',
            type: 'POST',
            data: jsonData,
            headers: {
                'Accept': 'application/json',
                'Content-Type': 'application/json'
            },
            dataType: 'json',
            complete: function (response) {
                console.log(response.responseText);
                if (response.responseText == 'success') {
                    console.log('Registration Success');
                    alert('Success');
                    $scope.msgalert = 'Registration Success, Proceed to Login and Continue';
                } else if (response.responseText == 'fail') {
                    alert('registration failed');
                    $scope.msgalert = 'Registration Failed, Please try again';
                }
            }
        });
    };
});

Also in index.html I have mentioned the controller:

<!-- HTML -->
<!DOCTYPE html>
<html ng-app="Main-app">
<!--  Controllers  -->
<script src="js/controllers/stackoverflow_q3.js" type="text/javascript"></script>

    <!-- Other elements -->

</html>

Any idea how can I do all these activities in just one "Submit" click?

3
  • Instead of ng-show=showme, try ng-show=msgalert. Commented Nov 19, 2015 at 17:44
  • 1
    Also, why are you calling in angular's $http function, but using jQuery's ajax function? Generally speaking, you'd want to create a service in angular and call that in your controller. Commented Nov 19, 2015 at 17:47
  • Yeah Jonathan, you are right, I agree on that. My bad. Commented Nov 19, 2015 at 18:33

2 Answers 2

4

Just do this: Inject $timeout service in your controller and wrap your code as following:

complete: function(response) {
    console.log(response.responseText);
    $timeout(function() {
        if (response.responseText == 'success') {
            console.log('Registration Success');
            alert('Success');
            $scope.msgalert = 'Registration Success, Proceed to Login and Continue';

        } else if (response.responseText == 'fail') {
            alert('registration failed');
            $scope.msgalert = 'Registration Failed, Please try again';
        }
    });
}

Also, change your HTML code like this:

<h3 ng-show="msgalert"><em>{{msgalert}}</em></h3>

So there is a concept of digest cycle in Angular. If you are doing something outside the Angular's context (like you are using jQuery AJAX to get the data and updating the $scope variable of msgalert), we have to tell Angular that something has changed in the data.

So after you get the response from the server and you updated the msgalert, Angular is unaware of that change since it is outside of the Angular's context so new digest cycle of Angular is triggered and the view is not updated.

Why you are seeing this is, when you click the submit button again in the form, then a digest cycle of Angular is triggered behind the scene and then your actual message is getting displayed.

To fix the problem, we wrapped your custom (outside angular context) code into the Angular's context using the $timeout service which is notifying the Angular immediately that the value in msgalert has changed

Optionally, you can write the $scope.$apply() after your if-else condition but it throws an exception sometimes that digest cycle is already in progress because calling $scope.$apply(), we are manually triggering the Angular's digest cycle. That is why the cleaner approach is to the $timeout service.

Update

Please note that you don't have to use jQuery at all to do that AJAX call. Angular has $http service and using it you will not face that problem at all. You can write your code like this:

$scope.submitForm = function (isValid, user) {
    var regData = {
        email: user.email,
        password: user.password1
    };

    $http({
        method: "POST",
        url: "myurl",
        data: regData,          // You don't need to stringify it and no need to set any headers
    }).then(function (response) {
        // Success callback
        console.log(response.responseText);
        if (response.responseText == 'success') {
            $scope.msgalert = 'Registration Success, Proceed to Login and Continue';
        } else if (response.responseText == 'fail') {
            $scope.msgalert = 'Registration Failed, Please try again';
        }
    }, function() {
        // Error callback
    });
};
Sign up to request clarification or add additional context in comments.

3 Comments

Great! This absolutely works. I had read about http, but it was giving me error for some reason. Now that I know the concept of message digest, I will implement again. Thanks!
Glad this helps and you understand this :-)
When I tried to implement $http , just like you mentioned, it gives me this error SyntaxError: Unexpected token e at Object.parse (native)
2

Your actual problem is jquery AJAX usage. Normaly when a AJAX call is being made by angular $http it calls a scope.digest cycle internally while ajax response found. But here you are calling jquery ajax so angular is not able to digest the changes so binding is not working.

Try the following after just before complete method finished.

$scope.$apply();

As follows

complete: function(response) {
              console.log(response.responseText);
              if(response.responseText == 'success'){
                console.log('Registration Success');
                   alert('Success');
                   $scope.msgalert='Registration Success, Proceed to Login and Continue';

              }
              else if(response.responseText == 'fail'){
                alert('registration failed');
                $scope.msgalert='Registration Failed, Please try again';

              }
              $scope.$apply();
 }

But it is better to use $http because it call $scope.$apply() internally.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.