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=&IncludeAcknowledged=onclick&Source=&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 }});
}
}
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.