C# MVC Detect which button was clicked

Detecting which button was clicked to cause a post back is very easy, once you know how to do it!

I often have screens that have multiple submit buttons on a single form that require a post back. I needed to pass the button value to the controller in MVC. For instance, I may have a data entry screen where a user can click a button that says “Save” or “Save as New”. I need to be able to detect which button they clicked when the page posts back.

Fortunately, there is an easy way to tell, or determine, which button the user selected when the page posts back on a submit action.

First, you need to have a button setup like so:

<form id="rvWidgetForm" method="post" enctype="application/x-www-form-urlencoded" asp-controller="Search" asp-action="SearchBegin">

    <button id="btnTopTen" type="submit" name="submitButton" class="btn text-center" value="TopTen">
        TOP 10 SEARCH
    </button>

    <button id="btnTraditional" type="submit" name="submitButton" class="btn  text-center" value="Trad">
        TRADITIONAL SEARCH
    </button>

</form>

The buttons must be of type=submit. Having type=button won’t post back. You have a choice here, to use the value= or not use it. If you don’t declare a value attribute, then what you’ll receive in the controller is the text of the button. While this is okay, you or another developer may change the text in the future and not realize they are going to break your code. I recommend using the value= like I’ve used above. It’s less likely to change in the future.

I recommend using the value attribute as it’s less likely than the button text to change in the future.

The next most important part is the name attribute. Every button that will post back should have the same name. This will be the name of your parameter in your controller as well and they must match. The value you declare on the button will be the argument passed to your controller method.

public async Task<ActionResult> SearchBegin([FromForm] SearchPageModel _searchModelIn, [FromForm] string submitButton)
    {
        // If you "name" your buttons as "submitButton"
        // then you can grab the value of the button
        // here to make decisions on which button was clicked
        switch (submitButton)
        {
            case "TopTen":
                return TopTen(_searchModelIn);
            case "Trad":
                return Traditional(_searchModelIn);
            default:
                break;
        }

        return View("~/");  // Go home

 }

The parameter name in your method must match the name attribute on your buttons exactly. The type passed in will be a string, although I imagine if your value attribute on all your buttons was numeric, that you could declare it as an int.

Once you’re in your method, you can use logic in a switch statement to detect the value passed in and make a decision how to proceed.

MVC AJAX JSON Null DTO in Controller

A few months ago, I was enabling paging on a .NET Core 3.1 MVC application and had my search model passed into a controller method via AJAX. Well, it didn’t work. I received a NULL DTO object no matter what I tried. Trying to figure out what to do about an MVC Ajax JSON Null DTO in a controller method had me chasing my tail.

Fast forward to a few days ago, and guess what, another web app, same use case, same issue. Problem was, I couldn’t remember how I resolved it. Well, after another two hours of tinkering around with different objects, removing default settings in my DTO, and more endless googling, I finally found the issue… again.

Main issue I had is that System.Text.Json is not really usable. I found out that unless all your properties are strings, you have to setup custom comparers for each type. That about sums it up. Unless you’re passing in a very simple object that only has string properties, you can pretty much forget about using this library out of the box.

Take a look at the things that are not supported in System.Text.Json: https://docs.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to

For those of you in a hurry, here is a summary of what I did. Details of the implementation will follow:

  1. Make sure you have “FromBody” in your controller method. I already had this, but it’s what most blog posts focus on.
[HttpPost]
public async Task<IActionResult> CatDisplay([FromBody] SearchModel<LuCategory> searchModelIn)

2. Change the default JSON serializer in your Startup.cs

using Microsoft.AspNetCore.Mvc;
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews().AddNewtonsoftJson();

If you get the little squigglies under this method name, then add the Nuget package: Microsoft.AspNetCore.Mvc.NewtonsoftJson

Just so you can see how I’m calling this, here is the Javascript/Jquery/JSON that I’m sending in:

    function GetPaging(ToPage) {

        var _url = "/@Model.controllerName/@Model.actionName";

        // Set the global values for sorting post back
        var searchModel = {};
        searchModel.SortColumn = '@Model.SortColumn';
        searchModel.PrevSortColumn = ''; // Leave blank so sorting doesn't kick;
        searchModel.CurrentPage = ToPage;
        searchModel.PageSize = @Model.PageSize;
        searchModel.SearchTerm = '@Model.SearchTerm';
        searchModel.SearchFilter = '@Model.SearchFilter';
        searchModel.SortDescending = '@Model.SortDescending';
        searchModel.ActiveOnly = '@Model.ActiveOnly';
        searchModel.RefId = @Model.RefId;
        searchModel.RefUniqueId = '@Model.RefUniqueId';

        $.ajax({
            type: "POST",
            url: _url,
            async: true,
            contentType: "application/json",
            data: JSON.stringify(searchModel),
            dataType: "html",
            success: function (result, status, xhr) {
                $("#gridPartial").html(result)
            },
            error: function (xhr, status, error) {
                alert("Result: " + status + " " + error + " " + xhr.status + " " + xhr.statusText)
            }
        });

    }

3. The last problem I ran into was boolean values. In the above example, the boolean value was coming from the Model, so there is no issue. However, if you are trying to get a boolean value from javascript or jquery, big problems. In order to be sure that what is being passed as a value can be deserialized into an object, you should have code like:

searchModel.SortDescending = $('descending').val().toLowerCase() != 'false') ? true : false;

This issue has hit me several times, so I’ve used this code to resolve it.

Thanks for reading and happy coding!