Friday, December 11, 2015

ASP.NET MVC: Getting data or partial view from a controller method with an Ajax call

Points of Interest


  • Fetch raw data (current time utc and ticks) from server
  • Fetch fully formed html created using partial view on the server
  • Result is retrieved as JSON object from the Server onto the client
  • Client code sets appropriate divs with the returned data
  • Uses $.getJSON jquery method (a short hand for $.ajax)
  • To get and execute script, $.getScript jquery method

Model

DateTime (now)

Controller - Method to display Main Page

public ActionResult Index()
{
return View();
}

Controller - Method to return JSON data

public JsonResult GetTime()
{
var now = DateTime.UtcNow;
string viewString = RenderViewToString(this.ControllerContext, "_TimePartialView", now, true);

return Json(new
{
TimeUtc = now.ToShortDateString() + " " + now.ToShortTimeString(),
TimeTicks = now.Ticks,
TimePartialView = viewString
}, JsonRequestBehavior.AllowGet);
}

Controller - Helper method to create partial view as string

static string RenderViewToString(ControllerContext context,
                                    string viewPath,
                                    object model = null,
                                    bool partial = false)
{
ViewEngineResult viewEngineResult = null;
if (partial)
viewEngineResult = ViewEngines.Engines.FindPartialView(context, viewPath);
else
viewEngineResult = ViewEngines.Engines.FindView(context, viewPath, null);

if (viewEngineResult == null)
throw new FileNotFoundException("View cannot be found.");

var view = viewEngineResult.View;
context.Controller.ViewData.Model = model;

string result = null;
using (var sw = new StringWriter())
{
var ctx = new ViewContext(context, view,
context.Controller.ViewData,
context.Controller.TempData,
sw);
view.Render(ctx, sw);
result = sw.ToString();
}
return result;
}

Partial View (_TimePartialView)

@model DateTime

<h2>Using Partial View from Server</h2>
<div class="row">
    <div class="col-md-4">
        <h3>Current Time UTC</h3>
    </div>
    <div class="col-md-4">
        <h3>Current Time Ticks</h3>
    </div>
</div>

<div class="row">
    <div class="col-md-4">
        @Model.ToShortDateString()  @Model.ToShortTimeString()
    </div>
    <div class="col-md-4">
        @Model.Ticks
    </div>
</div>

Main View

@{
    ViewBag.Title = "Home Page";
}

<h2>Using Raw Data from Server</h2>
<div class="row">
    <div class="col-md-4">
        <h3>Current Time UTC</h3>
        <div id="current_time_utc"></div>
    </div>
    <div class="col-md-4">
        <h2>Current Time Ticks</h2>
        <div id="current_time_ticks"></div>
    </div>
    <div class="col-md-4">
        <h3>Update Async</h3>
        @Html.ActionLink("Update", "GetTime", null, new { @class = "btn btn-primary", id = "UpdateBtn" })
    </div>
</div>

<hr/>

<div id="time_partial_view"></div>

@section Scripts {
    <script>
        function fmtJSON(data) {
            return JSON.stringify(data, null, 2);
        }

        $(function () {
            $('#UpdateBtn').click(function (event) {
                event.preventDefault();
                var url = "/Home/GetTime";
                var getting = $.getJSON(url, null);
                getting.done(function (json) {
                    console.log('got data\n' + fmtJSON(json));
                    $('#current_time_utc').html(json.TimeUtc);
                    $('#current_time_ticks').html(json.TimeTicks);
                    $('#time_partial_view').html(json.TimePartialView);
                });
                getting.fail(function (jqxhr, textStatus, error) {
                    console.log("FAIL handler\njqXHR:\n" + fmtJSON(jqxhr) +
                       "\ntextStatus:\n" + textStatus +
                       "\nerrorThrown:\n" + error);
                });
                getting.always(function () { console.log("remove wait cursor"); });
            });
        });

    </script>
}

Output Before Update


Output After Update

Successful Data Transmission

{
  "TimeUtc": "12/11/2015 5:23 PM",
  "TimeTicks": 635854514161656600,
  "TimePartialView": "<h2>Using Partial View from Server</h2>\r\n<div class=\"row\">\r\n    <div class=\"col-md-4\">\r\n        <h3>Current Time UTC</h3>\r\n    </div>\r\n    <div class=\"col-md-4\">\r\n        <h3>Current Time Ticks</h3>\r\n    </div>\r\n</div>\r\n\r\n<div class=\"row\">\r\n    <div class=\"col-md-4\">\r\n        12/11/2015  5:23 PM\r\n    </div>\r\n    <div class=\"col-md-4\">\r\n        635854514161656592\r\n    </div>\r\n</div>\r\n"
}

Failure

jqXHR:
{
  "readyState": 4,
  "responseText": "<!DOCTYPE html>\r\n<html>\r\n    <head>\r\n        ....
  [Exception: bad]\r\n   ASPNetMvcAjaxGet.Controllers.HomeController.GetTime() in c:\\Temp\\ASPNetMvcAjaxGet\\ASPNetMvcAjaxGet\\Controllers\\HomeController.cs:18\r\n   lambda_method(Closure , ControllerBase , Object[] ....",
  "status": 500,
  "statusText": "Internal Server Error"
}
textStatus:
error
errorThrown:
Internal Server Error