Search code examples
javascriptjqueryajaxangularjs2-way-object-databinding

Two-way data binding after an Ajax call


I have a problem with the AngularJS two-way data binding. I'll try to explain the issue as clearly as possible: I have a list of contact names. For each element I have an Edit button. When I click on that button I load the "clicked" full Contact from an Ajax call and then I show a window with some input fields linked to the Contact just retrieved ("phone", "email" etc.). This is the interesting piece of the view:

<div>    
    <div class="contact" data-ng-repeat="contact in contacts">
        <span>{{contact.label}}</span>
        <a href="" class="btn btn-xs btn-default" data-ng-click="openContactModal(contact.ID)">
            Edit
        </a>
    </div>
</div>

The click on the Edit button fires this function (present in the Controller):

var newContact = null;
$scope.openContactModal = function(contactID){  
    newContact = new Contact(contactID);
    newContact.onLoad().then(
        function(){
            //loading OK
            $('#myModal').modal('show');
        },
        function(){
            //loading Error

        }
    );
    $scope.newContact = newContact;
};

The call to new Contact(contactID) loads a contact from the Database with an Ajax call. I open the modal window at the end of the Ajax call (waiting for the AngularJS promise). In the modal, though, all fields are empty even though they are linked to the contact model (newContact.phone, newContact.email etc.). I've already checked that the Ajax call works fine (printing the resulted JSON). I suppose I'm missing something in the two-way data binding issue. The strange fact is that, if I try to fill the empty modal fields, the newContact model reacts well, as if the two-way data binding works well from the view to the model, but not the contrary. Thank you in advance!

EDIT: this is the service that retrieves the contact:

angular.module("app").factory("Contact", ["ContactDBServices", "$q",
    function(ContactDBServices, $q){
        return function(contactID){

            //the contact itself
            var self = this;

            var contactPromise = $q.defer();

            //attributi del contatto
            this.firstName = null;
            this.ID = null;
            this.lastName = null;
            this.phone = null;
            this.fax = null;
            this.mobile = null;
            this.email = null;
            this.web = null;

            //the Ajax call    
            var metacontact = ContactDBServices.find({ID:contactID}, 
                function(){
                    this.ID = contactID;
                    this.firstName = metacontact.contact_name;
                    this.lastName = metacontact.contact_last_name;
                    this.phone = metacontact.contact_phone;
                    this.fax = metacontact.contact_fax;
                    this.mobile = metacontact.contact_mobile;
                    this.email = metacontact.contact_email;
                    this.web = metacontact.contact_web;

                    //!!!THE FOLLOWING PRINTS ARE GOOD!!!!  
                    console.log(this.ID);
                    console.log(this.firstName);
                    console.log(this.lastName);
                    console.log(this.phone);
                    console.log(this.fax);
                    contactPromise.resolve("OK");
                },
                function(){
                    contactPromise.reject("Error");
                }
            );


            this.onLoad = function(){
                return contactPromise.promise;
            };

    }
}]);  

If I print the same values in the controller, though, all that values are undefined:

var newContact = null;
$scope.openContactModal = function(contactID){  
    newContact = new Contact(contactID);
    newContact.onLoad().then(
        function(){

            //!!!!!!!!THE FOLLOWING PRINTS ARE ALL UNDEFINED!!!!
            console.log(newContact.firstName);
            console.log(newContact.lastName);
            console.log(newContact.phone);
            console.log(newContact.fax);

            $('#myModal').modal('show');
        },
        function(){
            //loading Error

        }
    );
    $scope.newContact = newContact;
};

This is strange. It seems a sort of synchronization issue :-/ to be thorough here is an example piece of the modal:

<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
    <div class="modal-dialog modal-sm">
        <div class="modal-content">
            <div class="modal-header">
                <h2>Contact</h2>
            </div>
            <div class="modal-body">
                <label>
                    Name
                    <input class="form-control" id="new_contact_name" data-ng-model="newContact.firstName" placeholder="Name">
                </label>
                <!-- ...and so on -->
            </div>
        </div>
        <div class="modal-footer">
            <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
            <button type="button" class="btn btn-primary" data-dismiss="modal" data-ng-click="createContact()">Crea</button>
        </div>
    </div>
</div>


Solution

  • Eventually I found the mistake. It was a mistake of mine and it doesn't belong to AngularJS, but to Javascript: you'll note that, in the Contact service, I did:

    //the Ajax call    
    var metacontact = ContactDBServices.find({ID:contactID}, 
        function(){
            this.ID = contactID;
            this.firstName = metacontact.contact_name;
            this.lastName = metacontact.contact_last_name;
            this.phone = metacontact.contact_phone;
            this.fax = metacontact.contact_fax;
            this.mobile = metacontact.contact_mobile;
            this.email = metacontact.contact_email;
            this.web = metacontact.contact_web;
    
        },
        function(){
            contactPromise.reject("Error");
        }
    );
    

    clearly, writing this. in the callback function, I didn't affect the Contact values, but the function attributes! To solve this issue I had to change the callback this way:

    //the Ajax call    
    var metacontact = ContactDBServices.find({ID:contactID}, 
        function(){
            self.ID = contactID;
            self.firstName = metacontact.contact_name;
            self.lastName = metacontact.contact_last_name;
            self.phone = metacontact.contact_phone;
            self.fax = metacontact.contact_fax;
            self.mobile = metacontact.contact_mobile;
            self.email = metacontact.contact_email;
            self.web = metacontact.contact_web;
    
        },
        function(){
            contactPromise.reject("Error");
        }
    );
    

    where

    var self = this;
    

    outside the callback.