Search code examples
c#jqueryajaxasp.net-corerazor-pages

Does AJAX call reload my entire page in Razor Pages?


I'm trying to do an AJAX call from my Razor Pages page.

JavaScript:

$.ajax({
    type: 'POST',
    url: '?handler=Delete',
    data: {
        id: $(this).data('id')
    },
    beforeSend: function (xhr) {
        // Required for POST, but have same issue if I removed this
        // and use GET
        xhr.setRequestHeader('XSRF-TOKEN',
            $('input:hidden[name="__RequestVerificationToken"]').val());
    },
})
.fail(function (e) {
    alert(e.responseText);
})
.always(function () {
    // Removed to simplify issue
    //location.reload(true);
});

Code behind:

public async System.Threading.Tasks.Task OnPostDeleteAsync(int id)
{
    string userId = UserManager.GetUserId(User);
    var area = DbContext.Areas.FirstOrDefault(a => a.UserId == userId && a.Id == id);
    if (area != null)
    {
        DbContext.Remove(area);
        await DbContext.SaveChangesAsync();
    }
}

And, in fact, this works correctly and the item is deleted.

However, the $.ajax.fail() handler is called and the error indicates there was a NullReferenceException.

AJAX Error

The exception is raised in my markup (CSHTML file):

@if (Model.ActiveAreas.Count == 0)  @***** NullReferenceException here! *****@
{
    <div class="alert alert-info">
        You don't have any active life areas.
    </div>
}

The reason there is this exception is because all of the properties of my page model are null. They are null because my OnGetAsync() method is never called to initialize them!

My question is why is my markup executing as though I'm updating the entire page? I'm doing an AJAX call. I don't want to update the entire page. So I don't know why my OnGetAsync() would ever need to be called.


Solution

  • I think the problem is either in:

    • your always() callback function
    • the Click event
    • or your return statement.

    always():

    From Jakob Jenkov's tutorial on jQuery AJAX:

    The callback function passed to the always() function is called whenever the AJAX request finishes, regardless of whether or not the AJAX request succeeds or fails. The three parameters passed to the callback function will be either the same three parameters passed to done() or fail(), depending on whether the AJAX request succeeds or fails.

    From the jQuery .ajax() documentation:

    In response to a successful request, the function's arguments are the same as those of .done(): data, textStatus, and the jqXHR object. For failed requests the arguments are the same as those of .fail(): the jqXHR object, textStatus, and errorThrown.

    From the jQuery deferred.always documentation:

    Note: The deferred.always() method receives the arguments that were used to .resolve() or .reject() the Deferred object, which are often very different. For this reason, it's best to use it only for actions that do not require inspecting the arguments. In all other cases, use explicit .done() or .fail() handlers since the arguments will have well-known orders.

    Click event:

    I think your button is triggering a page refresh. so use preventDefault() in your click event.

    Example:

    $('...').on('click', function (event) {
      
        event.preventDefault();//add the prevent default.
    
        //Your code
    
    });
    

    Return Statement:

    If your OnPostDelete method has return Page(), then please change it to JsonResult or RedirectToPage.

    Example:

    public Task<IActionResult> OnPostDelete(int? id)
    {
        if (id == null)
        {
            return NotFound();
        }
    
        //Your Code
    
        return Page(); //Remove this line
    
        return new JsonResult ("Customer Deleted Successfully!"); // Use something like this as needed.
    
        return RedirectToPage("./Index"); //if you want to reload page then use this.
    }