So in MVC when you
create a view you pass data in two ways. One is the ViewBag. The ViewBag is a
dynamic object (ooohh) that essentially wraps a hashtable. The other way to
pass data is the model. The model is generally a strongly typed object. The
model is very cool, because you can post and have model binding regenerate the
server side model object. You can then pass that object back in to the View and
the page will be regenerated. Very elegant.
So you have a
divide. You have a bunch of untyped data in a glob and a strongly typed data
object that must be fully represented in input fields in the view so that it
can be successfully regenerated. Okay, but what if I want something in between
like a strongly typed bunch of data that isn't necessarily going to be editted
and posted back? For example, suppose I have a page where you pick your
favorite fruit. I have a drop down list. My model has an identifier to indicate
what fruit has been picked, but where do I store the list of all the fruit. I
could store it in the model, but what if I have validation. Unless I put the
whole list in some hidden input I can't use the wonderful mechanism I mentioned
above where I just dump the newly model bound object into the view. Dumping the
whole list into a hidden input seems wrong and could potentially be insecure.
So the other option
is to put the list in the ViewBag. That works great, but imagine that you are
picking your favorite one of many types of food. You are suddenly storing a
large amount of data in some big untyped blob. No compiler or intellisense to
warn you when you are messing up. No sense of structure of meaning given by a
typed object.
This issue really
bothered me and I came up with a solution which I am not super crazy about, but
it works okay. So the basics are that you create an object to represent the
data which isn't necessarily going to be in your model. For example:
public class
FixedModel
{
private List<Fruit> _fruits;
public List<Fruit> Fruits
{
get { return _fruits ?? (_fruits = new List<Fruit>()); }
}
}
You then create a
descendant of WebPageView with a property of this type:
public
abstract class MyPage<TModel> : WebViewPage<TModel>
{
public FixedModel FixedData { get; private set; }
protected override void InitializePage()
{
FixedData = ViewBag.FixedData;
base.InitializePage();
}
}
Now in your
Controller you put this new object into the ViewBag:
private void
GenerateFixedData()
{
FixedModel fixedModel = new FixedModel ();
fixedModel .Fruits.Add(new Fruit { Id = 1, Name = "Apple" });
fixedModel .Fruits.Add(new Fruit { Id = 2, Name = "Banana"
});
fixedModel .Fruits.Add(new Fruit { Id = 3, Name = "Pear" });
fixedModel .Fruits.Add(new Fruit { Id = 4, Name = "Mango" });
ViewBag.FixedData= fixedModel;
}
Now
you call this method from both your GET and POST actions. And inside your razor
view you can access this object like FixedData.Fruits and this will be typed instead of dynamic. You have to add @inherits MyPage<ActualModel> at the top to
make use of your custom WebViewPage.
What I would really
prefer is for the WebViewPage to take two classes as opposed to just the model.
Then you could avoid the special WebViewPage descendant and using the ViewBag
to transfer data.
No comments:
Post a Comment