How do I bind to a specific array element in ASP.NET MVC? - asp.net-mvc

I'm using ASP.NET MVC RC1 and trying to bind a textbox to an object property like so:
<%= Html.TextBox("Comments.Contacts[0].ContactName") %>
It seems like it should work, since this does:
<%= ((MuralProject)ViewData.Model).Comments.Contacts[0].ContactName %>
But alas, the result in the textbox is an empty string. Am I doing something wrong?

The first argument for the text box extension method sets the name of the input element that's eventually created and also tries to get an entry from ViewData/Model (for the model it uses TypeDescriptors / reflection) based on that argument.
The way it does this is just by splitting up the input string at the dots then checking the ViewDataDictionary for specific keys and the Model via reflection so in the case you give it'll try and look for Contacts[0] rather than Contacts and won't pick up your property.
To get around this you just need to supply the actual value of the object e.g.
Html.TextBox("Comments.Contacts[0].ContactName",
Model.Comments.Contacts[0].ContactName)
You can see this yourself if you look at the MVC source and take a peek at the ViewDataDictionary class.

Related

MVC ViewBag seems to automatically populate TextBox in my View

In my controller I have the following:
ViewBag.VendorName = vendorname;
In my View I have the following:
#Html.TextBox("VendorName")
It appears that the TextBox VendorName is being populated with the content of ViewBag.VendorName.
This is what I want but didn't know that from a ViewBag you can directly populate a TextBox.
Is this expected and why does this happen?
According to Steve Sanderson in Pro MVC:
HTML helper methods populate their value from the following places (in this order):
ViewData.ModelState["VendorName"].Value.RawValue
For string based helpers, the value parameter passed to the helper method, or if you didn't supply one, then ViewData.Eval["VendorName"] (and ViewBag.VendorName as you're seeing here)
For strongly typed helpers, the corresponding property value on your Model object

asp.net mvc implicit population of input fields

In our asp.net mvc I've created view with two partial views inside.
That view accepts model of some type, for example Customer.
First partial view doesn't have model because it is search form with empty field.
Second form is form with populated fields.
What I found out that on first view, if I have called input fields like properties in model and if I don't provide value for them, mvc implicitly binds values from model to the fields.
First I was thinking is some of kind of mistake, but then I've expiremented little bit with a code:
-I've added native input element with id and name called the same like model, input field is empty in browser
-If I try the same thing with Html.TextBox helper and don't provide value, mvc gets that value from my model object(by name of property/field) and in browser that field is populated.
Is this a bug or am I doing something wrong?
Thanx
That's by design.
I'd recomend reading:
http://asp.net/mvc
http://weblogs.asp.net/scottgu/archive/tags/MVC/default.aspx
and last but not least:
http://channel9.msdn.com/Events/MIX
especially mix10 has a tonn of sessions about mvc
all are good read and watch (-:
That is by design. If you send a model to a view and you're using the HTML input Helpers that come with ASP.NET MVC, they'll implicitly populate themselves from the model.
This is helpful in many situations. If you don't want this behavior, you can always NOT use the helpers or write your own simple helpers.

How can I create an ASP.Net MVC Helper to inspect other inputs on a form

I would like to create an extension helper with the following signature:
public static MvcHtmlString
BindMissingFor(this HtmlHelper
htmlHelper, Expression> expression )
I would like this method to reflect through the supplied expression model and look for bind-able properties that have not already been bound on the form.
The use case is I would like to have some Views that only allow the user to interact with a portion of my Model. But, I would like to persist the entire model between multiple views (a wizard).
My current solution is to use a hidden-input for each field I don't want displayed. I'll probably do the same thing with this extension method, but I would like it to do the work for me instead of copy/pasting.
Is there a way to inspect the current form for inputs/selects from within an HtmlHelper extension method?
There is no way an html helper to know what happens in other parts of your view such as inspecting other form fields unless you pass it as argument. Also it is not very clear what you mean by look for bind-able properties that have not already been bound on the form. For persisting state in a wizard you might take a look at the Html.Serialize helper currently situated in the MVC Futures assembly. The idea behind this helper is that it allows you to serialize some model object (marked as [Serializable]) as hidden field inside a form and get its value back in a controller action using the [Deserialize] attribute. Behind the scenes it uses WebForms ViewState. You can also encrypt it. It is a good way of persisting state on the client between multiple pages.

How to pass an unpersisted modified object from view back to controller without a form?

Short: how does modelbinding pass objects from view to controller?
Long:
First, based on the parameters given by the user through a search form, some objects are retrieved from the database.
These objects are given meta data that are visible(but not defining) to the customer (e.g: naming and pricing of the objects differ from region to region).
Later on in the site, the user can click links that should show details of these objects.
Because these meta data are important for displaying, but not defining, I need to get the previously altered object back in the controller.
When I use the default asp.net mvc modelbinding, the .ToString() method is used. This off course doesn't return a relevant string for recreating the complete object.
I would have figured the ISerializable interface would be involved, but this is not so.
How should I go about to get the desired effect? I can't imagine I'm the first one to be faced with this question, so I guess I'm missing something somewhere...
The default model binding takes form parameters by name and matches them up with the properties of the type specified in the argument list. For example, your model has properties "Price" and "Name", then the form would need to contain inputs with ids/names "Price" and "Name" (I suspect it does a case insensitive match). The binder uses reflection to convert the form values associated with these keys into the appropriate type and assigns it to the properties of a newly created object of the type specified by the parameter (again derived by reflection).
You can actually look at (and download) the source for this at http://www.codeplex.com/aspnet, although you'll have to drill down into the MVC source from there. I'd give a link to the DefaultModelBinder source, but the way they are constructed, I believe the link changes as revisions are introduced.
So, to answer your question, you need to have parameters (could be hidden) on your form that correspond to the properties of the object that you want to recreate. When you POST the form (in the view) to the controller, the binder should reconstitute an object of the specified type using the form parameters. If you need to do translation from the values in the form parameter to the object properties, you'll probably need to implement your own custom model binder.
[EDIT] In response to your second post:
Let's say that we want to have a link back to an action that uses a customized object. We can store the customized object in TempData (or the Session if we need it to last more through more than one postback) with a particular key. We can then construct the action link and provide the key of the object as value to the ActionLink in an anonymous class. This will pass back the key as a Request parameter. In our action we can use the key from this parameter to retrieve the object from TempData.
<%= Html.ActionLink( ViewData["CustomObject1",
"Select",
new { TempDataKey = ViewData["CustomObject1_Key"] }
) %>
public ActionResult Select()
{
Entity custObj = null;
string objKey = Request.Params["TempDataKey"];
if (!string.IsNullOrEmpty(objKey))
{
custObj = (Entity)TempData[objKey];
}
... continue processing
}
#tvanfosson
Thanks for your explanation, but what about links? (no forms involved)
Currently the Html.ActionLink(c=>c.Action(parameter), "label") takes objects as parameter. These have to be translated into URL parts. For this, MVC ALWAYS goes to the .ToString() method. I don't want to serialize my object in the ToString method.
Shouldn't I be able to somehow help the framework serialize my object? Say through the ISerialize interface or something?

How to map form values to an object for ASP.NET MVC HTTP-Post scenario?

i have a simple form for an ASP.NET MVC application. I have a form property that is named differently (for whatever reason) to the real property name.
I know there's [Bind(Exlcude="", Include="")] attribute, but that doesn't help me in this case.
I also don't want to have a (FormsCollection formsCollection) argument in the Action method signature.
is there another way I can define the mapping?
eg.
<%= Html.ValidationMessage("GameServer", "*")%>
results in ..
<select id="GameServer" name="GameServer">
<option value="2">PewPew</option>
</select>
this needs to map to..
myGameServer.GameServerId = 2; // PewPew.
cheers!
i believe you will need to define it in your controller arguments or else it wouldnt have any clue what to accept.
public ActionResult GameServer(string GameServer){
GServer myGameServer = new GServer();
myGameServer.GameServerId.ToString() = GameServer;
return View("GameServer");
}
you can pass in the name/id of the parameter your trying to go for on your view page, it will automagically know the value to recieve based on the id on your view.
What's wrong with having a FormCollection passed as argument? I had to do the same thing as you and I just excluded the GameServer property in the BindAttribute.
Another thing you have to note is that Html.ValidationMessage("GameServer", "*") won't work because the underlying model doesn't contain a GameServer property. You have to add it to the model. I don't know if there is a better way to do it, I didn't find but it was required to make ValidationMessage works
You can create you own ModelBinder (see how to) to do the custom mapping.
An overkill IMO, but you might have your reasons...

Resources