Search code examples
c#asp.net-mvcrenderaction

RenderAction method parameters are null after a postback


My problem is that the parameter values in the RenderAction GET method are null after any postback in the application (see below). Can you help me identify the problem or how to better structure the layout code maybe? I'm thinking of using the ViewBag more for the ids instead of URL parameters but would appreciate a quicker fix for now.

_Layout.cshtml

<!DOCTYPE html>

<html lang="nl">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>@ViewBag.Title</title>
    @Styles.Render("~/Content/css")
</head>
<body>
    @{ Html.RenderAction("LayoutMenu", "Example", new { pageTitle = ViewBag.Title }); }

    <div class="...">
        @RenderBody()
    </div>
    <footer class="...">
        @* ... *@
    </footer>

    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/jqueryval")
    @* ... *@
</body>
</html>

_LayoutMenu.cshtml

@model namespace.Models.LayoutMenu

<header class="...">
    <div class="">
        <div class="">
            <a href="@Url.Action("Index", "Home")"></a>
        </div>
        @if (Model != null && Model.Entities != null)
        {
            Html.RenderPartial("_Entities", Model.Entities);
        }
        <div class="">
            <nav class="">
                @{
                    var controller = ViewContext.ParentActionViewContext.RouteData.Values["Controller"].ToString();
                    var action = ViewContext.ParentActionViewContext.RouteData.Values["Action"].ToString();
                    var id1 = ViewContext.ParentActionViewContext.RouteData.Values["id1"];
                    var id2 = ViewContext.ParentActionViewContext.RouteData.Values["id2"];
                    var id3 = ViewContext.ParentActionViewContext.RouteData.Values["id3"];
                }
                <div>
                    <div class="@(controller == "Home" && action == "Index" ? "item--active" : "")">
                        @Html.ActionLink("Home", "Index", "Home", null, new { @class = "itemlink" })
                    </div>

                    <div class="@(controller == "Example" && action == "Index" ? "item--active" : "")">
                        @Html.ActionLink("Example", "Index", "Example", null, new { @class = "itemlink" })
                    </div>
                </div>
            </nav>
        </div>
    </div>
</header>
<div class="...">
    @if (!String.IsNullOrEmpty(Model.ExampleName))
    {
        <h2>@("Example - " + Model.ExampleName)</h2>
        @Html.ActionLink(Model.SelectedEntityName, "Index", "Home", null, new { @class = "..." })
        @Html.ActionLink(Model.ExampleName, "SomeEntities", "Example", null, new { @class = "..." })
    }
</div>
<div class="...">
    @if (id3 != null)
    {
        <div class="">
            <div class="@(controller == "Example" && action == "ExampleProperties" ? "tab-active" : "tab-line")">
                @Html.ActionLink("Properties", "ExampleProperties", "Example", null, new { @class = "itemlink" })
            </div>
            <div class="@(controller == "Example" && action == "OtherExampleProperties" ? "tab-active" : "tab-line")">
                @Html.ActionLink("Other Properties", "OtherExampleProperties", "Example", null, new { @class = "itemlink" })
            </div>
            @*...*@
        </div>
    }
    else if (id1 !=  null)
    {
        <div class="">
            <div class="@(controller == "Example" && action == "SomeExamples" ? "tab-active" : "tab-line")">
                @Html.ActionLink("Some Examples", "SomeExamples", "Example", null, new { @class = "itemlink" })
            </div>
            @*...*@
        </div>
    }
</div>

_LayoutMenu Action Get Method

[HttpGet]
[ChildActionOnly]
public ActionResult LayoutMenu(string selectedEntity, Guid? id1 = null, Guid? id2 = null, Guid? id3 = null)
{
    // my problem: id1, id2, id3 are null after a postback
    var result = new LayoutMenu();
    var entities = _entityHelper.GetEntities();

    if (!String.IsNullOrWhiteSpace(selectedEntity))
    {
        if (entities.Any(entity => entity.GetKey().Equals(selectedEntity)))
        {
            result.SelectedEntityName = entities.First(entity => entity.GetKey().Equals(selectedEntity))?.Name;
        }
        else
        {
            selectedEntity = null;
            result.SelectedEntityName = entities.First().Name;
        }
    }
    else
    {
        result.SelectedEntityName = entities.First().Name;
    }

    result.Entities = new Entities
    {
        EntitiesList = entities,
        SelectedEntity = selectedEntity
    };

    result.ExampleName = id1.HasValue ? _api.GetExample(id1.Value).Name : "";

    if (id2.HasValue && id3.HasValue)
    {
        var data = _api.GetData(id2.Value, id3.Value);
        // result.SomeMoreData = ...
    }

    return PartialView("_LayoutMenu", result);
}

The postback simplified view

@model namespace.Models.AddData

<div>
    @using (Html.BeginForm())
    {
        @*1st form input fields*@
        <input class="button" type="submit" name="submit" value="Search" />
        @*2nd form input fields in a table*@
        <input class="button" type="submit" name="submit" value="Create" />
    }
</div>

The postback simplified action method

[HttpPost]
public async Task<ActionResult> AddData(Guid id1, AddData model, string submit)
{
    if (!String.IsNullOrEmpty(submit))
    {
        ModelState.Clear();

        if (submit.Equals("Create"))
        {
            // model state checks...

            if (ModelState.IsValid)
            {
                // process and add data.

                // return new statuses
                return View(await CreateModel(id1));
            }

            return View(model);
        }
        else
        {
            // return search
            return View(await CreateModel(id1));
        }
    }
return RedirectToAction(nameof(AddData));
}

Solution

  • Apparently the order of the arguments in the LayoutMenu action method is important since when I put the ids first and then the selectedEntity and pageTitle (in my local version), it worked!

    It seems that the LayoutMenu post method (which is below) was being called on a postback and it returned the get method with null values for id1, id2 and id3. Once I added those parameters on the post method and returned them in the bottom, it worked!

    [HttpPost]
    public ActionResult LayoutMenu(string selectedEntity, string pageTitle, bool? isEntityChange/*, Guid? id1, Guid? id2, Guid? id3*/)
    {
        if (isEntityChange.HasValue && isEntityChange.Value)
        {
            if (!String.IsNullOrWhiteSpace(selectedEntity))
            {
                // set selected entity and go to home.
                // ...
                return Redirect(Url.Action("Index", "Home"));
            }
        }
    
        // this was the faulty code.
        return LayoutMenu(selectedEntity/*,id1, id2, id3*/);
    }