1

How do I initiate an ajax request (calling controller action) from razor view which returns the data in JSON format?

At the moment after clicking the action link in my razor view the page does the post request which redirects the page to /actionName which of course does not exist.

I am also using jQuery but not sure how do I fetch the data from razor view which needs to be passed if I use jQuery ajax method.

ShowEventLogs.cshtml

@{ ViewBag.Title = "Event Logs"; }
@model IEnumerable
<Application.Models.EventLogs>
<table id="data-table" class="table display responsive" style="width:100%">
   <thead class="thead-colored thead-light">
      <tr>
         <th>Time</th>
         <th>Scheme</th>
         <th>Serial Number</th>
         <th>Batch</th>
         <th>Exp Date</th>
         <th>Product Code</th>
         <th>Http Code</th>
         <th>Is Confirmed?</th>
         <th>Confirmation Date</th>
         <th>Verify Pack</th>
      </tr>
   </thead>
   <tbody>
      @foreach (var item in Model)
      {
      <tr>
         <td>@item.Timestamp</td>
         <td>@item.ProductCodeScheme</td>
         <td>@item.SerialNumber</td>
         <td>@item.Batch</td>
         <td>@item.ExpirationDate</td>
         <td>@item.ProductCode</td>
         <td>@item.HttpResponseCode</td>
         <td>@item.ConfirmedParsed</td>
         <td>@item.ConfirmedDate</td>
         if (@item.HttpResponseCode == "202")
         {
         <td class="text-secondary">@Html.ActionLink("Verify Pack", "VerifyPack", "Home", new { ProductCodeScheme = @item.ProductCodeScheme, ProductCode = @item.ProductCode, SerialNumber = @item.SerialNumber, Batch = @item.Batch, ExpirationDate = @item.ExpirationDate, CommandStatusCode = 0 }, new { @class = "text-info" })</td>
         }
         else
         {
         <td class="text-secondary">Not Available</td>
         }
      </tr>
      }
   </tbody>
</table>
}

Controller action

[HttpPost]
public ActionResult VerifyPack(string productCodeScheme, string productCode, string serialNumber, string batch, string expirationDate, int commandStatusCode, string orderTrackingNo = null) {
    string TextAreaResult = string.Empty;
    string TextAreaResultException = string.Empty;
    string TextAreaResultHttpOperationCode = string.Empty;
    string TextAreaResultHttpResponseCode = string.Empty;
    string TextAreaResultHttpInformation = string.Empty;
    string TextAreaResultHttpWarning = string.Empty;
    string TextAreaResultState = string.Empty;
    string RemoteIpAddress = string.Format("{0}", Request.UserHostAddress);

    try {
        using(SecureMediDatabase database = new SecureMediDatabase(this)) {
            DatabaseFactory.setDatabase(database);
            Request baseRequest = (Request) database.createRequest(Country);
            ServiceThread serviceThread = new ServiceThread(0, null, Country);
            serviceThread.attach(this);

            baseRequest.setId(0);
            baseRequest.setProductCodeScheme(productCodeScheme);
            baseRequest.setRequestType(1); //single pack
            baseRequest.setProductCode(productCode);
            baseRequest.setSerialNumber(serialNumber);
            baseRequest.setBatch(batch);
            baseRequest.setExpirationDate(expirationDate);
            baseRequest.setWorkstation(RemoteIpAddress);
            baseRequest.setManualEntry(string.IsNullOrEmpty(expirationDate) || string.IsNullOrEmpty(batch));

            if (baseRequest.isManualEntry()) {
                switch (commandStatusCode) {
                    case 2:
                    case 3:
                        break;

                    default:
                        throw new NotSupportedException("This operation does not support manual entries!");
                }
            }

            switch (Country) {
                case "SE":
                    SecureMediRequestSE requestSE = (SecureMediRequestSE) baseRequest;
                    requestSE.setUserId(@User.Identity.Name);
                    requestSE.setCommandStatusCode(commandStatusCode);
                    requestSE.OrderTrackingNumber = orderTrackingNo;
                    break;

                case "FI":
                    SecureMediRequestFI requestFI = (SecureMediRequestFI) baseRequest;
                    requestFI.setSubUserId(@User.Identity.Name);
                    break;
            }

            serviceThread.RunRequest(control, baseRequest, apteekki);

            TextAreaResult = string.Format("{0} {1} {2} {3} {4}", baseRequest.getResponseOperationCode(), baseRequest.getHttpResponseCode(), baseRequest.getHttpInformation(), baseRequest.getHttpWarning(), baseRequest.getResponseStatusCode());

            TextAreaResultHttpOperationCode = string.Format("{0}", baseRequest.getResponseOperationCode());

            TextAreaResultHttpResponseCode = string.Format("{0}", baseRequest.getHttpResponseCode());

            TextAreaResultHttpInformation = string.Format("{0}", baseRequest.getHttpInformation());

            TextAreaResultHttpWarning = string.Format("{0}", baseRequest.getHttpWarning());

            TextAreaResultState = string.Format("{0}", baseRequest.getResponseStatusCode());
        }
    } catch (Exception exc) {
        TextAreaResultException = "Exception: " + exc.Message;
    }

    return Json(new {
        result = TextAreaResult,
        httpOperationCode = TextAreaResultHttpOperationCode,
        httpResponseCode = TextAreaResultHttpResponseCode,
        httpInformation = TextAreaResultHttpInformation,
        httpWarning = TextAreaResultHttpWarning,
        state = TextAreaResultState,
        exception = TextAreaResultException,
        isSuccess = TextAreaResultHttpResponseCode == "200" || TextAreaResultHttpResponseCode == "202"
    });
}

Error based on the answer:

enter image description here enter image description here

2
  • Instead of Html.ActionLink, you will have to create your anchor tag yourself and then add an unobtrusive onclick to it using javascript. In your click handler, you will then have to manually build up the url to get and then do your $.ajax Commented Jan 16, 2019 at 0:46
  • Razer is nice for formatting a page using a .NET model but will be of little use when trying to tie in AJAX/JavaScript calls at all. Either use an anchor tag with the hacky href of "#!" or some other html element, a common class and use the JQuery click handler to pass w/e data needs to be sent to the controller. Commented Jan 16, 2019 at 0:51

2 Answers 2

2

Basically @Html.ActionLink() helper renders anchor tag (<a>) with attributes and defaulted to use GET request by refreshing whole page, hence you need to add preventDefault() in order to use AJAX callback from that element. If the action method uses HTTP GET method, you can perform simple AJAX call against common class of the anchor link like this:

$('.text-info').on('click', function (e) {
    e.preventDefault();

    var url = $(this).attr('href');
    $.get(url, function (response) {
        // do something with AJAX response
    });
});

However since target controller action marked as [HttpPost], you need to extract query string parameters from href attribute with additional function and use them in the AJAX call with type: 'POST' setting, or use $.post():

$('.text-info').on('click', function (e) {
    e.preventDefault(); // mandatory to prevent GET request

    var url = $(this).attr('href');

    var pcs = getQueryStringParams(url, 'ProductCodeScheme');
    var pc = getQueryStringParams(url, 'ProductCode');
    var sn = getQueryStringParams(url, 'SerialNumber');
    var batch = getQueryStringParams(url, 'Batch');
    var expDate = getQueryStringParams(url, 'ExpirationDate');
    var csc = getQueryStringParams(url, 'CommandStatusCode');

    // create key-value pair for action method parameters
    var obj = { ProductCodeScheme: pcs, ProductCode: pc, SerialNumber: sn, ... }

    $.ajax({
        type: 'POST',
        url: url.split('?')[0], // URL without query string, or use '@Url.Action("VerifyPack", "Home")'
        data: obj,
        dataType: 'json', // expects response as JSON
        success: function (response) {
            // do something with AJAX response
        },
        error: function (xhr, status, err) {
            // error handling
        }
    });

    // just make sure that the link is not redirecting
    return false;
});

function getQueryStringParams(url, name) {
     return (RegExp(name + '=' + '(.+?)(&|$)').exec(url)||[,null])[1];
}

Actually there exists another way to call AJAX from anchor tag like @Ajax.ActionLink(), depending on your choice:

@Ajax.ActionLink("Verify Pack", "VerifyPack", "Home", new { ProductCodeScheme = @item.ProductCodeScheme, ProductCode = @item.ProductCode, SerialNumber = @item.SerialNumber, Batch = @item.Batch, ExpirationDate = @item.ExpirationDate, CommandStatusCode = 0 }, 
                 new AjaxOptions { HttpMethod = "POST", 
                                   InsertionMode = InsertionMode.Replace, 
                                   UpdateTargetId = "targetElementId", 
                                   OnComplete = "onComplete();" 
                                 },
                 new { @class = "text-info" })

Note:

If you need to handle AJAX request and normal request from the same controller, you can differentiate them using Request.IsAjaxRequest() (or Context.Request.Headers["X-Requested-With"] == "XMLHttpRequest" in Core MVC).

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks. However, I am still getting redirected to /Home/VerifyPack after using your jQuery solution which extracts the string query parameters. I have added the error screenshot to the original question.
Sounds like you have problem with click event binding. Try to move e.preventDefault() to the bottom-most part of click event.
1

Something like this should get you started. Add a class to the items you need to pull information from. Then instead of using an actionlink just create a normal a element with a unique class as well. Have JQuery handle click events on those links and pass the other TD items of the same row to the controller via an AJAX call.

$(".button").click( function() {
	var tr = $(this).closest("tr");
  var ProductCodeScheme = tr.find(".ProductCodeScheme").html();
  var SerialNumber = tr.find(".SerialNumber").html();
  var Batch = tr.find(".Batch").html();
  var ExpirationDate = tr.find(".ExpirationDate").html();
  var ProductCode = tr.find(".ProductCode").html();
  
  $.ajax({
    url: "/Verify Pack/VerifyPack", 
    type: "POST",      
    data: ({
    	ProductCodeScheme: ProductCodeScheme,
      SerialNumber: SerialNumber,
      Batch: Batch,
      ExpirationDate: ExpirationDate,
      ProductCode: ProductCode
    }),     
    cache: false,
    success: function(data){                          
      //Do something here for a successful POST                   
    }           
  });    
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table id="data-table" class="table display responsive" style="width:100%">
   <thead class="thead-colored thead-light">
      <tr>
         <th>Time</th>
         <th>Scheme</th>
         <th>Serial Number</th>
         <th>Batch</th>
         <th>Exp Date</th>
         <th>Product Code</th>
         <th>Http Code</th>
         <th>Is Confirmed?</th>
         <th>Confirmation Date</th>
         <th>Verify Pack</th>
      </tr>
   </thead>
   <tbody>
      <tr>
         <td>Timestamp 1</td>
         <td class="ProductCodeScheme">ProductCodeScheme 1</td>
         <td class="SerialNumber">SerialNumber 1</td>
         <td class="Batch">Batch 1</td>
         <td class="ExpirationDate">ExpirationDate 1</td>
         <td class="ProductCode">ProductCode 1</td>
         <td>HttpResponseCode 1</td>
         <td>ConfirmedParsed 1</td>
         <td>ConfirmedDate 1</td>
         <td class="text-secondary"><a href="#!" class="button">Item 1</a></td>
      </tr>
     <tr>
         <td>Timestamp 2</td>
         <td class="ProductCodeScheme">ProductCodeScheme 2</td>
         <td class="SerialNumber">SerialNumber 2</td>
         <td class="Batch">Batch 2</td>
         <td class="ExpirationDate">ExpirationDate2</td>
         <td class="ProductCode">ProductCode 2</td>
         <td>HttpResponseCode 2</td>
         <td>ConfirmedParsed 2</td>
         <td>ConfirmedDate 2</td>
         <td class="text-secondary"><a href="#!" class="button">Item 2</a></td>
      </tr>
   </tbody>
</table>

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.