AJAX

AJAX with ASP.NET 5

The next feature I wanted to handle moving my contacts application from ASP.NET 4 to ASP.NET 5 was not doing a full page refresh when applying a filter. For my implementation in ASP.NET 4 check out the Partial View and Partial View with AJAX posts.

For the partial view there is currently no menu choice but it is easy enough to create the view manually. In fact my _ContactList.cshtml looks exactly as it did in ASP.NET 4.

Rendering a partial view has changed slightly to use an async using await. The following is what replaced the contact list display in the contact index page.

<div id="contactList">
    @{
        await Html.RenderPartialAsync("_ContactList");
    }
</div>

At this point I was ready to add in AJAX to refresh my partial view when the user changes filters. In ASP.NET 4 there was the Ajax.BeginForm helper for making Ajax requests, but this does not currently exist in ASP.NET 5.

After a lot of searching I came across the jquery-ajax-unobtrusive github repo. This is the same library that was being used by Ajax.BeginForm ASP.NET 4 and it is available using Bower which ASP.NET 5 and Visual Studio 2015 have great support for.

Just by adding the following line to the dependencies section of my project’s bower.json the needed files were automatically download. The great thing about making edits in bower.json is that intellisense works!

"jquery-ajax-unobtrusive": "3.2.3"

The next step was to add the following line to the copy task in my project’s gulpfile.js.

"jquery-validation-unobtrusive": "jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"

I spent a good bit of time getting the gulp task to work. This is my first usage of gulp and at this point I still don’t know if there is an indicator that the copy failed other than the file the file not being at the specified location. My problem ended up being a typo of a dot instead of a dash.

With jquery-ajax-unobtrusive.js file in place the script needs to be include in _Layout.cshtml found in the Views\Shared folder. In _Layout.cshtml you will notice sections have been added for different environments. For the time being I have only change the scripts Development section with the following.

<script src="~/lib/jquery-ajax-unobtrusive/jquery.unobtrusive-ajax.js"></script>

The Staging and Production environment share the same set of script sources by default but can be separated if needed. The default setup also uses a CDN to pull scripts from by default with fallbacks to the local version. Before an actual deployment make sure to include jquery-ajax-unobtrusive.js in the Staging and Production environments.

Back in Contacts\Index.cshtml the filter form needs to change to the following.

@using (Html.BeginForm("Index", "Contacts", FormMethod.Post, new
{
    id = "filterButton",
    data_ajax = "true",
    data_ajax_method = "POST",
    data_ajax_mode = "replace",
    data_ajax_update = "#contactList"
}))
{
    <p>
        @Html.TextBox("Filter")
        <input type="submit" value="Filter"/>
    </p>
}

This is the standard Html.BeginForm with some added HTML attributes to support the Ajax call. The data_ajax* work because of jquery-ajax-unobtrusive. To get the values needed I ran the ASP.NET 4 version of my application and used view source to determine what values I needed. If you take the approach you will notice in the view source from the browser the data attributes will contain dashes which need to be changed to underscores for use in a razor view.

The last bit that needs to change is the index action of the contacts controller.

public IActionResult Index(string filter)
{
   var contacts = from c in _db.Contacts
                  select c;

   if (!string.IsNullOrEmpty(filter))
   {
     contacts = contacts.Where(c => c.Name.Contains(filter) ||
                                    c.Address.Contains(filter) ||
                                    c.City.Contains(filter) ||
                                    c.Email.Contains(filter) ||
                                    c.Phone.Contains(filter) ||
                                    c.State.Contains(filter) ||
                                    c.ZipCode.Contains(filter));
   }

   if (Request?.Headers != null && 
       Request.Headers["X-Requested-With"] == "XMLHttpRequest")
   {
     return PartialView("_ContactList",contacts);
   }

   return View(contacts);
}

The only gotcha here when moving from ASP.NET 4 is that the request object does not currently contain an IsAjaxRequest extension. Checking the X-Requested-With key of the request headers for XMLHttpRequest indicates an ajax request and triggers the return of the _ContactList partial view.

You should now have a working ajax request. It took me a while to get this all worked out I hope it saves you some time. Information on ASP.NET 5 can be sparce, but this will improve over time.

AJAX with ASP.NET 5 Read More »

Partial View with AJAX

This is step two of applying filters to my contact list without refreshing the whole page. I will be using the partial view created in last week’s post.

I ran into a bit of trouble getting ajax working. It seems that starting in MVC 5 the jQuery plugin needed to make this form of ajax work is no longer included by default. In order to fix use NuGet and install jQuery.Unobtrusive.Ajax.nuget-jqueryajax

After the NuGet package is installed add a new bundle (or add to an existing bundle) for the jQuery unobtrusive files to the BundleConfig found in the App_Start folder.

bundles.Add(new ScriptBundle("~/bundles/jqueryunob").Include(
            "~/Scripts/jquery.unobtrusive*"));

If a new bundle was added then make sure to render the new bundle in the _Layout.cshtml file in the Views/Shared folder.

@Scripts.Render("~/bundles/jqueryunob")

The next step was to change the index action of contacts controller. The index action now needs to accept both gets and posts which can be accomplished via the AcceptVerbs attribute. The second change needed in the index action is to return a partial view instead of a view if the request is an ajax request.

[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
public ActionResult Index(string city, string search)
{
    var cityList = new List<string>();
    var cityDistinct = from c in db.Contacts
                       orderby c.City
                       select c.City;

    cityList.AddRange(cityDistinct.Distinct());
    ViewBag.city = new SelectList(cityList);

    var contacts = from c in db.Contacts
                   select c;

    if (!string.IsNullOrWhiteSpace(search))
    {
        contacts = contacts.Where(c => c.Name.Contains(search));
    }

    if (!string.IsNullOrWhiteSpace(city))
    {
        contacts = contacts.Where(c => c.City == city);
    }

    if (Request.IsAjaxRequest())
    {
        return PartialView("_ContactList", contacts);
    }

    return View(contacts);
}

Reusing the index action is only one option to implement the needed changes. Another option would be to add another action to the controller that would return the partial view. In this case the  common query code would be moved to a function.

The last set of changes needed are in the index view. Instead of using Html.BeginForm Ajax.BeginForm should be used. The section of the page that will be replaced via the ajax request needs to be moved to a div with an id. In the example below I added a div with the id of contactList. It is important that the div id match the UpdateTargetId in AjaxOptions.

@model IEnumerable<Contacts.Models.Contact>

@{
    ViewBag.Title = "Index";
}

<h2>Index</h2>

<p>
    @using (Ajax.BeginForm("Index",
                           "Contacts",
                           new AjaxOptions
                           {
                               UpdateTargetId = "contactList"
                           }))
    {
        <p>
            City: @Html.DropDownList("city",
                                     ViewBag.city as SelectList,
                                     "All",
                                     new {@class = "city",
                                          onchange = "$(this.form).submit();"})
            Name: @Html.TextBox("Search")
            <input type="submit" value="Filter"/>
        </p>
    }
</p>
<div id="contactList">
    @{
        Html.RenderPartial("_ContactList");
    }
</div>

One thing to make special note of is the that the onchange for the city drop down list has change from “this.form.submit();” to “$(this.form).submit();”. Without this change the request will not come through as an ajax request and the full page will refresh instead of just the contact list section. I wasted a lot of time trying to track down why a full page request was happening and the issue ended up being the way that the drop down list was submitting the form.

Partial View with AJAX Read More »