0

I apologize if this is a duplicated question somewhere but I could not find an answer that works.

I have an .aspx page that is calling a webservice which populates basic employee information. It works great when I call the service outside of a custom object. As soon as I move the ajax functionality into the custom object, I lose scope and cannot populate the object properties. I can see that the service is returning data in the json via Firebug but the object's properties do not get populated. Any help on this is greatly appreciated. The custom object JavaScript is below.

/* Begin Employee JavaScript object */
//Define the Employee object
function Employee() {}

//Define the Employee object's properties on the prototype
Employee.prototype = {
    //Properties
    EmployeeNumber: '',
    FirstName: '',
    LastName: '',
    ErrorMessage: '',
    IsFound: false
}

//Define the Search function
Employee.prototype.employeeSearch = function (pEmplNo, callback) {
    $.ajax({
        context: this,
        type: "POST",
        url: "webservice URL",
        data: '{"pEmployeeNumber":"' + pEmplNo + '"}',
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        success: function (msg) {
            var me = this;
            me.EmployeeNumber = msg.d.EmployeeNumber;
            me.FirstName = msg.d.FirstName;
            me.LastName = msg.d.LastName;
            me.ErrorMessage = '';
            me.IsFound = true;
            callback(me);
        },
        error: function () {
            var me = this;
            me.ErrorMessage = 'Error retrieving employee';
            me.IsFound = false;
        }
    });
}
/* End Employee JavaScript object */

function SetEmployeeFields(pEmployee) {
    $('#<%= txtEmployeeNumber.ClientID %>').val(pEmployee.EmployeeNumber);
    $('#<%= txtFirstName.ClientID %>').val(pEmployee.FirstName);
    $('#<%= txtLastName.ClientID %>').val(pEmployee.LastName);
}

function FindEmployee(emplNo) {
    $("#mdlGetEmployeeDialog").dialog('open');

    if (emplNo.length < 6) {
        while (emplNo.length < 6) {
            emplNo = "0" + emplNo;
        }

        $('#<%= txtEmployeeNumber.ClientID %>').val(emplNo);
    }

    var _employee = new Employee();
    _employee.employeeSearch(emplNo, function () {
        if (_employee.IsFound) {
            SetEmployeeFields(_employee);
            $('#<%= txtNature.ClientID %>').focus();
            $("#mdlGetEmployeeDialog").dialog('close');
        }
        else {
            $('#<%= lblMessage.ClientID %>').text('Could not find employee' + _employee.ErrorMessage);
            $('#<%= upErrors.ClientID %>').addClass("ui-state-error").css({ "padding": "4px", "margin-top": "4px" }).show();
            $("#mdlGetEmployeeDialog").dialog('close');
        }
    });
}

//Sets up the Employee Search modal and button
function SetupGetEmployeeModal() {
    $("#mdlGetEmployeeDialog").dialog({
        autoOpen: false,
        modal: true,
        width: 500
    });
    $("#btnEmployeeSearch").button({
        text: false,
        icons: {
            primary: "ui-icon-search"
        }
    }).css({
        'padding': '4px 0px 4px 0px',
        'font-size': '9px',
        'margin-left': '3px'
    });
}

$(function () {

    $('#btnEmployeeSearch').click(function (e) {
        e.preventDefault();
        var emplNo = $('#<%= txtEmployeeNumber.ClientID %>').val();

        if (emplNo !== '' && emplNo !== '######') {
            $('#<%= lblMessage.ClientID %>').text('');
            $('#<%= upErrors.ClientID %>').removeClass('ui-state-error').removeAttr('style').hide();
            FindEmployee(emplNo);
        }
        else {
            $('#<%= lblMessage.ClientID %>').text('Employee Number required');
            $('#<%= upErrors.ClientID %>').addClass("ui-state-error").css({ "padding": "4px", "margin-top": "4px" }).show();
            $("#mdlGetEmployeeDialog").dialog('close');
        }
    });
});
/* End Page Scripts */
fujiiface
  • 1,262
  • 11
  • 15
  • Ajax is asynchronous. as written, `if (_employee.IsFound) {` will always happen before the ajax request completes. You need to use callbacks. – Kevin B Apr 09 '14 at 17:35

4 Answers4

1

Ajax is asynchronous. You need to add a callback to your employeeSearch method so that you can act upon it's success.

Employee.prototype.employeeSearch = function (pEmplNo,callback) {
    ...
            success: function (msg) {
                this.EmployeeNumber = msg.d.EmployeeNumber;
                this.FirstName = msg.d.FirstName;
                this.LastName = msg.d.LastName;
                this.ErrorMessage = '';
                this.IsFound = true;
                callback(this);
            },
    ...
}

Now, use the callback later:

var _employee = new Employee();
_employee.employeeSearch(emplNo, function () {
    if (_employee.IsFound) {
        SetEmployeeFields(_employee);
        $("#mdlGetEmployeeDialog").dialog('close');
    }
    else {
        $('#<%= lblMessage.ClientID %>').text('Could not find employee'+ _employee.ErrorMessage);
        $('#<%= upErrors.ClientID %>').addClass("ui-state-error").css({ "padding": "4px", "margin-top": "4px" }).show();
        $("#mdlGetEmployeeDialog").dialog('close');
    }
});
Kevin B
  • 94,570
  • 16
  • 163
  • 180
  • Thanks for the help @KevinB. This did the trick. I was getting my "Could not find employee" text before the ajax finished up so you were dead on. Once I added the callback, it worked as expected. Marked as answer. – fujiiface Apr 09 '14 at 18:08
0

You need to scope your variables inside your callback functions:

success: function (msg) {
    this.EmployeeNumber = msg.d.EmployeeNumber;
    this.FirstName = msg.d.FirstName;
    this.LastName = msg.d.LastName;
    this.ErrorMessage = '';
    this.IsFound = true;            
},
error: function () {
    this.ErrorMessage = 'Error retrieving employee';
    this.IsFound = false;
}
patrickmcgraw
  • 2,465
  • 14
  • 8
0

EDIT:

So it looks like your properties are getting set, but since it's asynchronous, it is not populated yet, you should move this to the ajax callback:

if (_employee.IsFound) {
        SetEmployeeFields(_employee);
        $("#mdlGetEmployeeDialog").dialog('close');
    }
    else {
        $('#<%= lblMessage.ClientID %>').text('Could not find employee'+ _employee.ErrorMessage);
        $('#<%= upErrors.ClientID %>').addClass("ui-state-error").css({ "padding": "4px", "margin-top": "4px" }).show();
        $("#mdlGetEmployeeDialog").dialog('close');
    }
OtotheA
  • 513
  • 4
  • 12
  • I originally had this and it did not make a difference. I have added it back to my code. I have created the object properties and functions on the prototype so that they are not recreated for each object and also to teach myself something new. I am used to just writing out JS instead of organizing it into reusable objects. – fujiiface Apr 09 '14 at 17:51
  • Fair enough. I just think this is prettier. Do you have a functional demo with the errors up anywhere? – OtotheA Apr 09 '14 at 18:01
  • I didn't have a functional demo up but @Kevin-B had the right idea. I was checking a property before it was set so it resulted in my error text displaying and then the ajax request being sent off. Once I added a callback and moved the _employee.IsFound check within it, I got the expected results. See the marked answer. I also updated the code according to suggestions here. Thanks for your input into all of this. – fujiiface Apr 09 '14 at 18:16
0

Aside from scoping your properties, as Kevin-B pointed out, you have to execute the code that comes after the call to employeeSearch, in the success method.

Javascript is asynchronous so after the ajax call is made, the rest of the code is executed.

Another thing you can do is use a library that help you make synchronous calls. Using such a library you can make your code to execute when the ajax call has returned.

Take a look here.

Another quick fix is to add a flag variable that you set only when the success or error functions were called, and add a while loop with that flag variable as condition right after the function call.

Employee.prototype.employeeSearch = function (pEmplNo) {
     $.ajax({
        context: this,
        type: "POST",
        url: "webservice URL",
        data: '{"pEmployeeNumber":"' + pEmplNo + '"}',
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        success: function (msg) {
            var me = this;
            me.EmployeeNumber = msg.d.EmployeeNumber;
            me.FirstName = msg.d.FirstName;
            me.LastName = msg.d.LastName;
            me.ErrorMessage = '';
            me.IsFound = true;   
            me.searchEnded = true;
        },
        error: function () {
            var me = this;
            me.ErrorMessage = 'Error retrieving employee';
            me.IsFound = false;   
            me.searchEnded = true;
        }
     });
     while(!this.searchEnded)
     {
     }
}

Also, if you ever need to call a function with a different scope, take a look at apply and call.

Community
  • 1
  • 1
Andrei
  • 3,086
  • 2
  • 19
  • 24
  • I originally used this and it made no difference. @kevin-b is on the right track since my "Could not find employee" text is displayed before I get into my response. Is there a reason you assign this to a variable instead of just using it directly? – fujiiface Apr 09 '14 at 17:49
  • 1
    Because "this" is not guaranteed to remain the same. It is best practice to assign it to a variable. – Andrei Apr 09 '14 at 17:51
  • Thanks for the references and clarification. That makes sense. – fujiiface Apr 09 '14 at 18:04
  • You are welcome. Btw, i added another way to do it, without a callback , if for any reason you don't like callbacks. – Andrei Apr 09 '14 at 18:10