Checking to see if ViewBag has a property or not, to conditionally inject JavaScript
Asked Answered
B

6

35

Consider this simple controller:

Porduct product = new Product(){
  // Creating a product object;
};
try
{
   productManager.SaveProduct(product);
   return RedirectToAction("List");
}
catch (Exception ex)
{
   ViewBag.ErrorMessage = ex.Message;
   return View("Create", product);
}

Now, in my Create view, I want to check ViewBag object, to see if it has Error property or not. If it has the error property, I need to inject some JavaScript into the page, to show the error message to my user.

I created an extension method to check this:

public static bool Has (this object obj, string propertyName) 
{
    Type type = obj.GetType();
    return type.GetProperty(propertyName) != null;
}

Then, in the Create view, I wrote this line of code:

@if (ViewBag.Has("Error"))
{
    // Injecting JavaScript here
}

However, I get this error:

Cannot perform runtime binding on a null reference

Any idea?

Beliabelial answered 27/12, 2011 at 3:39 Comment(4)
What code is actually generating that error?Millard
@JohnSaunders, as you see I've set the ViewBag.Error in my controller? How it can be null?Beliabelial
@AndrewBarber, this line @if (ViewBag.Has("Error"))Beliabelial
@SaeedNeamati: I don't know. Why don't you find out? Try @if (ViewBag == null) and see what the result is.Imponderabilia
C
22

Your code doesnt work because ViewBag is a dynamic object not a 'real' type.

the following code should work:

public static bool Has (this object obj, string propertyName) 
{
    var dynamic = obj as DynamicObject;
    if(dynamic == null) return false;
    return dynamic.GetDynamicMemberNames().Contains(propertyName);
}
Charla answered 10/9, 2012 at 16:16 Comment(3)
Good suggestion @JeffreyABecker. Thanks, but I get Cannot perform runtime binding on a null reference exception.Beliabelial
Seems that I should use it like ((Object)ViewBag).Has("PropertyName").Beliabelial
Resharper suggests "return dynamic != null && dynamic.GetDynamicMemberNames().Contains(propertyName);" instead of the last 2 lines. Nice solution! Thanks!Loxodromics
C
100
@if (ViewBag.Error!=null)
{
    // Injecting JavaScript here
}
Cyrilcyrill answered 22/6, 2012 at 8:9 Comment(5)
Dear @jazzcat, please first check your code, then send it. If I haven't set ViewBag.Error in my controller, which means that ViewBag won't have a property called Error at runtime, then I'll encounter an exception.Beliabelial
@SaeedNeamati, jazzcat's code worked fine for me. For ViewBag, any item not found will be null. Remember that dynamic ultimately has some actual code behind it. ViewBag is a DynamicViewDataDictionary, which apparently decides to return null for non-existing "properties" rather than throwing an error.Linden
This should be the chosen answer!Siftings
Actually, you can improve this with a bit of other magic; @if(ViewBag.Error ?? false). This it will work if you set a boolean or not.Ludovico
MVC has a DynamicViewDataDictionary which the ViewBag inherits that implements this null-return behavior. Note that if you're using the Razor Engine outside of MVC, you'll get an exception if you try to access an undefined property rather than its returning null. See github.com/jlamfers/RazorMachine/issues/35Tanning
C
22

Your code doesnt work because ViewBag is a dynamic object not a 'real' type.

the following code should work:

public static bool Has (this object obj, string propertyName) 
{
    var dynamic = obj as DynamicObject;
    if(dynamic == null) return false;
    return dynamic.GetDynamicMemberNames().Contains(propertyName);
}
Charla answered 10/9, 2012 at 16:16 Comment(3)
Good suggestion @JeffreyABecker. Thanks, but I get Cannot perform runtime binding on a null reference exception.Beliabelial
Seems that I should use it like ((Object)ViewBag).Has("PropertyName").Beliabelial
Resharper suggests "return dynamic != null && dynamic.GetDynamicMemberNames().Contains(propertyName);" instead of the last 2 lines. Nice solution! Thanks!Loxodromics
I
4

Instead of using the ViewBag, use ViewData so you can check for the of the item you are storing. The ViewData object is used as Dictionary of objects that you can reference by key, it is not a dynamic as the ViewBag.

// Set the [ViewData][1] in the controller
ViewData["hideSearchForm"] = true;    

// Use the condition in the view
if(Convert.ToBoolean(ViewData["hideSearchForm"])
    hideSearchForm();
Islas answered 5/6, 2012 at 19:58 Comment(5)
How? Give us an example at least ;).Beliabelial
Still ViewBag seems more elegant than ViewData. I don't like string indexes.Zandrazandt
The problem that was the dynamic viewbag does not allow you to check if the property exists, in which case the ViewData is the correct choice. However in mvc4 are some improvements to the view bag support. beletsky.net/2012/04/new-in-aspnet-mvc4-razor-changes.htmlIslas
@Martin What meaningful difference is there between a string index and a dynamic member's name? ExpandoObject is just a dictionary under the covers, for instance.Readership
@emodendroket - it's mostly just visual difference, the code looks a bit cleaner.Zandrazandt
E
3

I would avoid ViewBag here completely. See my thoughts here on this: http://completedevelopment.blogspot.com/2011/12/stop-using-viewbag-in-most-places.html

The alternative would be to throw a custom error and catch it. how do you know if the database is down, or if its a business logic save error? in the example above you just catch a single exception, generally there is a better way to catch each exception type, and then a general exception handler for the truly unhandled exceptions such as the built in custom error pages or using ELMAH.

So above, I would instead ModelState.AddModelError() You can then look at these errors (assuming you arent jsut going to use the built in validation) via How do I access the ModelState from within my View (aspx page)?

So please carefully consider displaying a message when you catch 'any' exception.

Enthymeme answered 27/12, 2011 at 4:2 Comment(7)
It looks like I'm "necrothreading" this :-) I've read your article, and I really use strong-typed models in MVC 4... But I don't have any qualms against using ViewBag for simple things. Most people coming from C#, and other strong-typed languages frown upon dynamics due to their heritage (I'm also a 'strong-typed' guy). But it all comes down to TESTING! If your views are tested the dynamic variables won't matter. In fact it's much better to use ViewBag everywhere and test it heavily than rely on strong-types to assure correctness. Good testing make the choice of dynamics vs strong-types moot.Bookbindery
I love JavaScript, however I'm constantly bit by its loose typing. Dynamic variables matter. There's hidden behavior behind the scenes with viewbag in the helpers where any values take precedence to anything in a model. Secondly, they are brittle for refactoring and finding references and type assignments, etc. Testing isn't a catch all and testing is only how good your tests are. No one's tests are 100%, not at any place or project I've ever seen and that's many. Its a best effort and you can surely introduce bugs that aren't caught by tests, to which you update your tests to handleEnthymeme
this new fix, but that still doesn't prevent it. While I'm happy you posted a response, I cant agree with this: "In fact it's much better to use ViewBag everywhere and test it heavily than rely on strong-types to assure correctness" Ask any architect whose been using MVC on many projects if they agree with that statement. ViewModels is the preferred method for a whole list of reasons. Why would it be better to use ViewBag and test heavily, rather than use a property in a viewmodel, and simply not have to worry about it?Enthymeme
Adam, I think it is a lot overstated the contribution of strong-typing towards correctness. Unit testing (and TDD) is really the main evolution/contribution towards it in ages. While I'm more comfortable inside a strongly-typed environment, I came to "accept" that the dynamic guys really changed my assumptions that strong-typing equals less bugs/more robustness. They had no "language" help, so they had to rely solely on unit testing. That's why the serious Javascript projects have so few bugs (jQuery, node.js, CoffeeScript). And that's why I don't mind passing a Title on the ViewBag :-)Bookbindery
'Serious' javascript projects also have bugs. jQuery has had thousands in its lifetime. Testing hasn't stopped the bugs. If you are using VieWModels, by definition the pattern should contain information for the view, otherwise you are losing a well established benefit of using them, I can't argue that point. Many architects have figured that out, thats why viewmodels is the best practice approach. It doesnt mean yours wont work, but you do open up for 1. inconsistency in using viewmodels, and #2. all the prior mentioned stuff.Enthymeme
Sure, as devs we can decide how we want, but established arch. best practices are there usually for a reason. Again, not pointing and saying 'you are wrong' as there are many approaches, but consider those that have gone before you and why the practices existEnthymeme
Adam, here is not the place for this discussion (mea culpa!), but don't believe for a single nanosecond I don't agree with you, and I'm a LOT more comfortable working with View Models... My point is just that strong-typing is not what really helps most to ensure correctness. Many people advocate it as a means towards that goal. I don't agree with it. It has a LOT of benefits, mostly because the tools in VS work well with strong-types but not with dynamic ones (take refactoring for example), so, in "our" environment, strong-typing is the way to go! (unless it is just a view Title :-)Bookbindery
F
3

You can use ViewData.ContainsKey("yourkey").

In controller:

ViewBag.IsExist = true;

In view:

if(ViewData.ContainsKey("IsExist")) {...}
Frascati answered 29/4, 2015 at 16:13 Comment(0)
K
0

I need to test this but:

@if (ViewBag.ABoolParam ?? false)
{
    //do stuff
}

I think will give you either the value of the ViewBag property, or return a default value if missing.

Keef answered 15/7, 2021 at 18:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.