Sniptaculous, a C# snippet library

Sniptaculous, a C# snippet library

I’ve just released the first version of Sniptaculous, a Visual Studio C# snippet library.

I think the existing snippets are great. They have parameters and some of them, such as the switch snippet (sw), will expand and make all of the switch cases for you if you use an existing enum as the parameter.

Since I love snippets and I find they speed up development, I’ve made some of my own. Over 50 snippets in fact.

I will continue adding to and refining Sniptaculous. I’m always open to suggestions and pull requests.

Error handling in ASP.NET MVC

This is a basic error handling strategy for an ASP.NET MVC app. It will handle all ASP.NET errors, allow somewhere to put logging code and redirect to a generic error page.

This will miss exceptions not handled by ASP.NET such as navigating to an invalid URL but will be sufficient to handle and log application errors in your controllers.

First start by creating an ErrorController (this one is basic but could be expanded):

using System.Web.Mvc;

namespace BasicErrorHandling.Controllers
{
    public class ErrorController : Controller
    {
        // GET: Error
        public ActionResult Index(string message)
        {
            TempData["errorMessage"] = message;
            return View();
        }
    }
}

Don’t forget to create a view for said controller:

@{
    Layout = null;
}

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Error</title>
</head>
<body>
    <div> 
        <h1>Generic Error Page</h1>
        <p>@TempData["errorMessage"]</p>
        
    </div>
</body>
</html>

For testing purposes, let’s introduce an exception in one of our actions:

public ActionResult About()
{
    ViewBag.Message = "Your application description page.";

    // throw an error to test exception handling
    throw new Exception("Fictious error");

    return View();
}

The method that will handle our errors and redirect to our error page is in our Global.asax.cs:

protected void Application_Error(object sender, EventArgs e)
{
    // get error and clear it 
    var exception = Server.GetLastError();
    Server.ClearError();

    // log error
    // ...

    // redirect to error page
    string message = Regex.Replace(exception.Message, @"\t|\n|\r", ""); // replace newlines which will not work with query string
    Response.Redirect("/Error/?message=" + message);
}

Here we should log or handle the error in the manner appropriate to our application. Finally we redirect to an error page supplying an optional message parameter. We could also redirect to this error controller from somewhere else than Global.asax.

To make everything work nicely we need to add a new route in our RouteConfig.cs:

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapRoute(
        name: "Error",
        url: "Error/{message}", 
        defaults: new { controller = "Error", 
                        action = "Index", 
                        message = UrlParameter.Optional } 
    );

    routes.MapRoute(
        name: "Default",
        url: "{controller}/{action}/{id}",
        defaults: new { controller = "Home", 
                        action = "Index", 
                        id = UrlParameter.Optional }
    );
}

 
You can find the whole thing on GitHub.

Simple Ajax scenario in ASP.NET MVC Part 3: graceful degradation

This follows part 1 and part 2.

Let’s continue improving our previous example by adding more requirements. This time supporting clients without JavaScript aka graceful degradation for our Ajax feature.

Graceful degradation in web design refers to supporting less capable clients correctly. In our case, using Ajax will currently only work in browsers with JavaScript enabled.

While that is most browsers, the best scenario possible is to support all browsers, even those who have disabled JavaScript or do not support it.

Supporting clients without JavaScript is sometimes required for conforming to standards or for accessibility reasons.

Changing our previous example

If we disable JavaScript and try our current version of our example application our filtering feature doesn’t work anymore (as well as our jQuery DataTables plugin).

jsdisabled

We are currently using the onclick event on a checkbox. Since this won’t submit the form without JavaScript we need to show the input element we had hidden in Part 2 when JS is disabled.

We still want to have that element hidden if JavaScript is enabled so instead of relying on style=”display: none” we will use JavaScript to hide the input element.

using (Ajax.BeginForm("FilterDepartmentsAjax",
                       new AjaxOptions
                       {
                           HttpMethod = "POST",
                           InsertionMode = 
                               InsertionMode.Replace,
                           UpdateTargetId = 
                               "department-table",
                           OnSuccess = "tableUpdated",
                       }))
{
    <label>Filter: </label>
    @Html.CheckBoxFor(model => model.Filtered, 
                      new { onclick = "$(this).parents('form:first').find(':submit')[0].click();" })
    <input id="input-filter" type="submit" value="Submit" />
}

<div id="department-table">
    @{ Html.RenderPartial("DepartmentsTablePartial", Model); }    
</div>

@section Scripts
{
    <script type="text/javascript">
        $(document).ready(function () {
            $('#input-filter').hide();
            $('#deparment-table').DataTable();
        });

        function tableUpdated() {
            $('#deparment-table').DataTable().draw();
        }
    </script>    
}

Now our button is shown only when JavaScript is disabled.

The next problem is that we want to return a PartialView when we are dealing with an Ajax request and a View when we are dealing with a regular request.

Otherwise we will either get only the partial view with JavaScript disabled or loose our jQuery DataTables functionality when we return a whole view.

The correct this we change the FilterDepartmentsAjax method return type to ActionResult and use if (Request.IsAjaxRequest()) like so:

[HttpPost]
public ActionResult FilterDepartmentsAjax(
                       DepartmentsModel model)
{
    var updatedModel = new DepartmentsModel
    {
        Filtered = model.Filtered,
        Departments = GetDepartments(model.Filtered),
    };

    if (Request.IsAjaxRequest())
        return PartialView("DepartmentsTablePartial", 
                           updatedModel);

    return View("Index", updatedModel);
}

Problem solved.

You can find the whole project on GitHub and the specific changeset for this blog post.

 

Simple Ajax scenario in ASP.NET MVC Part 2: integrating jQuery DataTables with our Ajax call

This follows part 1.

My previous example of filtering a table was pretty basic. Let’s complicate things a little bit. While online examples are often simplistic, programming a real application isn’t.

Let’s integrate the common jQuery DataTables plugin to our table. Doing so will provide many functionalities but will break our last example.

Let’s see how and what we can do to fix it.

First install jQuery DataTables. You can get the NuGet package, download the files online and include them manually in your project or link to the CDN.

For the sake of simplicity I will link to the CDN in my _Layout.cshtml file.

    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, 
          initial-scale=1.0">
    <title>@ViewBag.Title - ASP.NET Application</title>
    @Styles.Render("~/Content/css")
    @Scripts.Render("~/bundles/modernizr")

    <link rel="stylesheet" 
          type="text/css" 
          href="//cdn.datatables.net/1.10.12/css/jquery.dataTables.css">

And later in this same file…

    @Scripts.Render("~/bundles/jquery")
    <script type="text/javascript" 
            charset="utf8" 
            src="//cdn.datatables.net/1.10.12/js/jquery.dataTables.js">
    </script>
    @Scripts.Render("~/bundles/bootstrap")

Now let’s call the plugin on the page with our existing table by adding this at the end of Index.cshtml:

@section Scripts
{
    <script type="text/javascript">
        $(document).ready(function () {
            $('#deparment-table').DataTable();
        });
    </script>    
}

And adding an id to our table in our DepartmentsTablePartial.cshtml partial view.

<table id="deparment-table" class="table table-striped">

As we can see we now have a full featured data table with filtering, sorting and paging.

datatables1

Sadly when we make our Ajax call to get all the data instead of a filtered subset we loose the DataTables functionalities.

datatables2

This is because jQuery DataTables isn’t aware of our Ajax update. We are rewriting the content inside the div but we aren’t making the plugin aware of this fact. Luckily there is a function available in the plugin API to handle that scenario.

We need to change our Ajax Helper BeginForm call to include an OnSuccess function:

@using (Ajax.BeginForm("FilterDepartmentsAjax",
               new AjaxOptions
               {
                   HttpMethod = "POST",
                   InsertionMode = InsertionMode.Replace,
                   UpdateTargetId = "department-table",
                   OnSuccess = "tableUpdated",
               }))

And our Scripts section will now contain a tableUpdated function.

<script type="text/javascript">
    $(document).ready(function () {
        $('#deparment-table').DataTable();
    });

    function tableUpdated() {
        $('#deparment-table').DataTable().draw();
    }
</script>    

It might be counter-intuitive that we aren’t using the Ajax functions of DataTables but this is because we our handling the Ajax call through our MVC controller. The MVC Ajax Helper is already updating the table and we just want DataTables to take notice of it.

DataTables ajax.reload() expect a JSON data source and we have none to give it. We simply want to data table to redraw itself.

If we start out site again everything is now working:

datatables3

As before you can find the code on GitHub here and the specific change set here.