Displaying Bootstrap Alerts with ASP.NET MVC forms

When working with forms in ASP.NET MVC, particularly with the Ajax.BeginForm helper method, it is often useful to display some sort of indication to the user describing the success or failure of the operation. Actually, this is typically the case with all Ajax workflows since there are no page refreshes.

This post describes the use of the jquery-ajax-alerts utility for displaying Bootstrap-styled alert messages to the user. The utility contains a single CSS stylesheet and a simple JavaScript file. When using with an ASP.NET MVC project you can add these files to your page using a bundle or by simply adding them to the page after the requisite jQuery script.

Demo

Workflow

The typical workflow might consist of adding, editing, or deleting an item from a server and then displaying the updated content, whether a list of items or an updated item, in the client browser. Using the utility to display an alert requires creating the proper response object from the server.

Controller

Each action method on the controller that will be used with a form should return a JSON object containing the following properties:

POST, PUT

{ 
    success: <boolean>, // whether operation was successful
    message: <string>, // success/failure message
    content: <string> // HTML markup to render in the page
}

DELETE

{
    success: <boolean>, // whether operation was successful
    message: <string>, // success/failure message
    id: <int|string|guid> // the Id of the item that was deleted
}

You can use the Json method on the controller to return the response as a JsonResult.

return Json(new
{
    success = true,
    message = string.Format(Geocrest.Web.Mvc.Resources.FormMessages.PutSuccess, "model"),
    content = this.RenderPartialViewToString("list", this.Repository.All<T>().ToList())
});

And here is the implementation (on the controller) to render a partial view to a string:

/// <summary>
/// Renders the partial view as a string.
/// </summary>
/// <param name="viewName">Name of the view.</param>
/// <param name="model">The model.</param>
/// <returns>A string containing the rendered HTML.</returns>
public string RenderPartialViewToString(string viewName, object model)
{
    this.ViewData.Model = model;
    try
    {
        using (StringWriter sw = new StringWriter())
        {
            ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(this.ControllerContext, viewName);
            ViewContext viewContext = new ViewContext(this.ControllerContext, viewResult.View, this.ViewData, this.TempData, sw);
            viewResult.View.Render(viewContext, sw);

            return sw.GetStringBuilder().ToString();
        }
    }
    catch (System.Exception ex)
    {
        return ex.ToString();
    }
}

View

Now that the controller is set up to return the proper response you need to update the markup for the Ajax form. The BeginForm helper method takes an AjaxOptions parameter that has several properties for determining what JavaScript functions will be called during the various steps of the Ajax operation (i.e. begin, complete, success, failure). Use these properties to specify the respective methods on the utility script (and don't forget to add the script/stylesheet to the page).

POST

@using (Ajax.BeginForm("create", "somecontroller", new { area = "admin" }, new AjaxOptions 
{
    HttpMethod = "Post",
    OnSuccess = "AjaxAlerts.onSuccess(data, '#mylist', '#mymodal', '#btnSave')",
    OnFailure = "AjaxAlerts.onFailure(xhr, status, error, '#btnSave')",
    OnBegin = "AjaxAlerts.onBegin(xhr, '#btnSave')",
    OnComplete = "AjaxAlerts.onComplete"
}, new { @class = "form-horizontal" }))
{
    // your form content here
}

where the following are optional:

  • #mylist refers to an element that will render new content (in this case a partial view containing a table listing all items including the newly POSTed item.
  • #mymodal refers to the id of a Bootstrap modal to hide after the form is submitted (in the case where the form is displayed within a modal).
  • #btnSave refers to the button used to submit the form. The button will switch to and from it's loading state before and after the form is submitted.

DELETE

@using (Ajax.BeginForm("delete", "somecontroller", new { area = "admin" }, new AjaxOptions
{
    HttpMethod = "Delete",
    Confirm = "Delete this record? Are you sure?",
    OnSuccess = string.Format("AjaxAlerts.onDelete(data, '#row-{0}')", item.Id),
    OnFailure = "AjaxAlerts.onFailure(xhr, status, error, '')",
    OnBegin = "AjaxAlerts.onBegin(xhr, '')",
    OnComplete = "AjaxAlerts.onComplete"
}, new { style = "display:inline;" }))
{
    // your form content here
}

where the page contains a list of items with id attributes formatted as row-{id}. Upon deletion on the server, the element with the corresponding id will be removed from the DOM.

PUT

@using (Ajax.BeginForm("edit", "somecontroller", new { area = "admin" }, new AjaxOptions
{
    HttpMethod = "Put",
    OnSuccess = "AjaxAlerts.onSuccess(data, '', '', '#btnSave')",
    OnFailure = "AjaxAlerts.onFailure(xhr, status, error, '#btnSave')",
    OnBegin = "AjaxAlerts.onBegin(xhr, '#btnSave')",
    OnComplete = "AjaxAlerts.onComplete"
}, new { @class = "form-horizontal" }))
{
    // your form content here
}

Using the above approach works well when using MVC and the Ajax.BeginForm method but it works just as well if your working in a pure client-side application. You can use jQuery's ajax method and set the beforeSend, complete, success, and error callback functions in the same manner. Addtionally, the AjaxAlerts object is available in Asynchronous Module Definition format so you can require it in your code like this:

require(["jquery", "ajaxalerts"], function($, AjaxAlerts) {
    // do something and then alert the user using a custom data object.
    AjaxAlerts.onSuccess({ success: true, message: 'hello world' }, '', '', '');
    
    // or use it with jQuery ajax
    $.ajax("someurl", {
        type: 'POST',
        data: myDataObject,
        success: AjaxAlerts.onSuccess(data, '', '', '#btnSave'),
        complete: AjaxAlerts.onComplete,
        error: AjaxAlerts.onFailure(xhr, status, error, '#btnSave'),
        beforeSend: AjaxAlerts.onBegin(xhr, '#btnSave')
    });
});
comments powered by Disqus