Search code examples
javascriptasp.net-mvcviewbag

Viewbag changes value in onclick?


So I'm using a viewbag in a pretty large MVC application. (I know Viewbag isn't that great but I had to continue programming in the same style as previous developers for obvious reasons)

Today I stumbled upon a very weird problem with viewbag to me. I was trying to call a javascript function in an onclick function using the viewbag values as parameters. The function does a redirection, the function works fine but for some reason my viewbag value changes??

The IncludeAcknowledged value changes from True to onclick in the function.

Here is my code I used (with some temporary debug thingies): html:

@ViewBag.IncludeAcknoweldge @*displays true*@
<button class="btn btn-default" type="button" onclick="javascript:Common.loadUrl('IncidentMonitor/List/?Date=@ViewBag.DateTime&IncludeAcknowledged=@ViewBag.IncludeAcknoweldged&Source=@ViewBag.Source&SourceId=@ViewBag.SourceId');">@Resources.ButtonCancel</button>
@ViewBag.IncludeAcknoweldged @*displays true*@

I can also see True when placing a breakpoint near it. But in the inspector of the web-browser it just shows:

<button class="btn btn-default" type="button" onclick="javascript:Common.loadUrl('IncidentMonitor/List/?Date=&amp;IncludeAcknowledged=onclick&amp;Source=&amp;SourceId=');">Annuleren</button>

Any idea why this is happening? I can probably work around but would really like to know why it behaves like this.

edit: did a workaround by doing:

<button class="btn btn-default" type="button" onclick='loadUrl()'>@Resources.ButtonCancel</button>
<script>
function loadUrl() {
    javascript:Common.loadUrl("IncidentMonitor/List/Date=@ViewBag.DateTime&IncludeAcknowledged=@ViewBag.IncludeAcknoweldged&Source=@ViewBag.Source&SourceId=@ViewBag.SourceId");
    }
</script>

But would still like to know an answer why the workaround is correct while putting the function in the onclick gives the weird viewbag value.

edit2: As requested several times the controller code of the action... although there is not much to see because I never touched my variable elsewhere:

[HttpGet]
        [AuthorizeFunctionality(RequestedFunctionality.Editor)]
        public ActionResult Edit()
        {

            try
            {

                var editIncidentEventContainer = new EditIncidentViewModel
                {
                };

                ViewBag.IncludeAcknoweldged = true;

                return View("_Edit", editIncidentEventContainer);
            }
            catch (Exception ex)
            {
                return new RedirectToRouteResult(new RouteValueDictionary{{ "controller", "Error" },
                    { "action", "DisplayMessage" },
                    { "message", ex.Message }});
            }
        }

Solution

  • The behavior you seeing is an unfortunate side affect of a feature added in MVC-4/Razor-2.0 called conditional attributes.

    Prior to MVC-4, if you wanted to manually generate (say) a checkbox and set the checked attribute based on a model property, if was necessary to include messy conditional statements in the markup to set the checked attribute. With conditional attributes, you can simply use

    <input type="checkbox" checked="@ViewBag.IsChecked" ... />
    

    and if IsChecked is true it will output checked="checked" and if false the attribute will be omitted.

    In MVC-4+, you now need to use .ToString() to force razor to evaluate the value as a string rather than a bool, in your case

    onclick="...&IncludeAcknowledged=@ViewBag.IncludeAcknoweldged.ToString()&..."
    

    Note that the reason your second option works is that its not being evaluated inside a html attribute.

    You can read more about conditional attributes in this article.