ASP.NET MVC 3 and HTML Helper Extensions - asp.net-mvc

I am trying to work out whether I am misunderstanding something about ASP.NET MVC or whether I have found some sort of bug in ASP.NET MVC Beta 3. I am having a problem with a PartialView picking up the wrong model when using HTML Helper extensions
My controller code looks like this:
public ActionResult EditGeneral(MapGeneralViewModel vm)
{
var query = MapGeneralViewModel.ToModel(vm, svcMaps);
return PartialView("General", MapGeneralViewModel.FromModel(query));
}
In the case of this being an insert, the property vm.Id starts out as -1 and after the call to MapGeneralViewModel.ToModel it has been persisted the database and query.Id has a proper value.
The call to MapSettingsViewModel.FromModel returns a new viewmodel and I have checked that the Id property correct contains the newly created id value.
The relevant bits of the view look like:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<AdminWebRole.Models.Map.MapGeneralViewModel>" %>
<%: Model.Id %>
<%= Html.Hidden("IdTest", Model.Id) %>
<%= Html.HiddenFor(model => model.Id) %>
If I put a breakpoint in the view, Model.Id is correctly set to the right value.
The actual output of the controller (when Model.Id == 70) looks like this:
70
<input id="IdTest" name="IdTest" type="hidden" value="-1" />
<input id="Id" name="Id" type="hidden" value="-1" />
So the value output without using the HTML helpers is correct, but the values output by the helpers somehow is picking up the viewmodel that was passed into the controller !
I have no idea how this is happening. I have tried various things:
Using View() rather than PartialView()
assigning the results of MapGeneralViewModel.FromModel() to vm and then passing vm to the view
using <%: and <%=
setting vm to null (the old view model somehow still gets used)
changing the value of the incoming id to 0 (results in 0 being output in the view instead of -1)
the problem isn't specific to properties called "Id", I have also tested other fields with the same result
Am I confused over how this is supposed to work or have I hit a beta bug ? If it makes any difference, this is running inside my local Azure runtime on a Win7 64 bit machine.

There is no way that MVC is just picking up a variable that you haven't explicitly passed to your view or have hanging around in Session or TempData.
Since you're setting that Id to -1 I'm start there for problems.
The other possibility is that -1 is hanging around in your ModelState someplace. The HTML helpers look to ModelState first before deciding to use any values you pass in.

I have just come across a similar issue in a partial view that I use for displaying the items in an order.
I was using the following to render a hidden input.
#Html.Hidden("salesorderlineid", orderLine.SalesOrderLineID)
This works fine until I delete an item, when the ID of the wrong (deleted) item is used. I double-checked the model, this was correct and rendered correctly in other usages on the same view.
I coded the hidden input directly as html and it works fine - looks like an MVC3 bug perhaps?
<input type="hidden" name="salesorderlineid" value="#orderLine.SalesOrderLineID"/>

Related

Asp Mvc one way model property, i.e. from client only

Is there an attribute or something similar in Asp MVC to indicate that a property should only come from the client, i.e. it shouldn't be roundtripped.
So
#Html.HiddenFor(x => x.MyProp)
I set this somehow on the client together with some other properties, it goes the server, server has a model error so sends it all back. I don't want this one property to be roundtripped.
The best I can think of is to do this
<input type="hidden" name=MyPropOneWay />
Then I manually insert it at the controller end into the property
(Addition)
It's not to avoid model errors, it's because there are a few ways of submitting the form, this is a shortcut method. In no circumstance do I want this value to be roundtripped as otherwise it will look like it's been set when it hasn't, however I do want everything else to be roundtripped
Thanks
If you don't want the value rendered from the server (either initially or after a roundtrip), use:
<input type="hidden" name="#Html.NameFor(m => m.MyProp)" />
This will render an input hidden element without a value, but it will still have the correct name to automatically bind to your model on form submission. Which effectively makes it "one way".
(The NameFor helper was added in MVC4)

Misbehaving Asp.net MVC helper methods (ie: Html.TextBox() and Html.Hidden())

I've been trying to debug an issue and I pinned pointed it down to this scenario:
When the statement Html.TextBox("ID", "What the heck..") is executed, I expect it to render:
<input id="ID" name="ID" type="text" value="What the heck.." />
But I get a Guid as its TextBox value such as so:
<input id="ID" name="ID" type="text" value="2e369d2c-071d-4733-8382-cc9e77d0b912" />
Why is Asp.net MVC outputting Guids? I'm not overriding asp.net mvc's framework methods. Please refer to the screenshot.
Update:
Here's another screenshot using Html.Hidden() instead of Html.TextBox(). I couldn't use Html.HiddenFor() directly into the Watch window because HiddenFor() uses lambdas.
#Eric Petroelje and #TLS: You two are correct. TextBox() and Hidden() is retrieving ID's value from the POST variables and not from the current Model or the function's value parameter. Though, I've expected different from HiddenFor() and TextBoxFor(). I expected it to get its value from the POST'ed variables only if it cant get it from the current Model. How can I achieve this?
Maybe you have a POST variable named ID that is a GUID? If that's the case, the HTML Helper method will use that POST value. If no POST value is present, it will fall back to the value in the model.
You are correct that Html.TextBox("ID", "What the heck..") is expected to output the attribute values that you give in your first example; however, if you are using the Html.TextboxFor method, then you're using a MVC Helper that dynamically loads the value of the ID property and places that into the value attribute in the HTML. When you use the Html.TextboxFor method, your second example is the expected HTML output if your ID property is a Guid.

MVC3 ModelBinding to a collection posted back with index gaps

I have a collection of objects on my Model that I'm rendering in a View by using EditFor function, and I have an EditorTemplate which is responsible for actually rendering each object.
#Html.EditorFor(model => model.MyObjects)
This has worked well for a while now, and when you check the html, my text boxes are prefixed with the model property, followed by the index of the collection they came from.
<input class="text-box single-line" id="MyObjects_2__SomeProperty"
name="MyObjects[2].SomeProperty" type="Text" value="" />
However I've recently started using the ShowForEdit and ShowForDisplay properties in the model metadata for the collection, and in the first line of my editor template if the ShowForEdit is not true, I just skip it.
#if (!ViewData.ModelMetadata.ShowForEdit)
{
return;
}
But because these are all indexed in the html, when I try to save this collection back to the viewmodel via a postback, it fails because of a reliance on the indexing numbers. Every item in the collection after the missing index is missing from my view model when I check it's value.
In this case it's actually my first item in the collection that I'm skipping since I don't want it to be visible on the edit view, but because of this when I postback the first index in the html is 1 (instead of 0 like it normally would be), but this is a problem when you try to save the changes. This is also a problem when altering the DOM using javascript.
Has anyone else encountered a problem with the default model binder's ability to read data posted back when one or more indexes in the html represented collection are not present?
Are there model binders that handle this problem?
Ran into this issue recently and solved it by converting the List to a Dictionary<string, model> with GUIDs as the key.
#foreach (var index in Model.EmailAddresses.Keys)
{
<label asp-for="#Model.EmailAddresses[index].Email">Email</label>
<input asp-for="#Model.EmailAddresses[index].Email" type="text" />
}
This avoided having to include hidden inputs that map to the index value.
There are some very good blog posts that allow you to modelbind to a list without the need to provide zero based contiguous index. plz have a look at
http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/
http://zahidadeel.blogspot.com/2011/05/master-detail-form-in-aspnet-mvc-3-ii.html
Furthermore, if you are interested in MVVM pattern and knockout js you can check this great work by steve sanderson
For more reading put "editing varibale length list mvc style" in google and it will give u a dozen useful links

hidden form element problem - asp.net mvc

I create a strongly typed form like this in my controller:
return View("BlaForm", Bla);
In the view I use something like this:
(1)
<%= Model.Version %>
(2)
<%= Html.Hidden("Version", Model.Version)%>
Here (1) is just for debugging purposes.
After a successive update of my object this produces something like this:
(1)
10
(2)
<input id="Version" name="Version" type="hidden" value="9" />
The hidden value is out of synch for some strange reason ???!!! The Version value was definitely 10 in this case as established by the debugger. Why is this? Are hidden value somehow cached?
Thanks.
Christian
PS:
I also do:
if (TempData["ViewData"] != null)
{
ViewData = TempData["ViewData"] as ViewDataDictionary;
}
in the controller action to maintain form values in case validation errors happen. This seems to be the reason. But still I explicitely do: <%= Html.Hidden("Version", Model.Version)%> .... ???? Maybe I missunderstand the lif cycle a bit?
Html helper will always use the value in the GET or POST request before the value in your model or ViewData. This means that if you post Version=9 to a controller action and inside this action you try to modify its value to 10, when you return the View, the Html.Hidden helper will use the POSTed value and not the one in your Model. The only workaround is a custom HTML helper or simply:
<input id="Version" name="Version" type="hidden" value="<%= Model.Version %>" />
HTML helper will always look for values in ModelStateDictionary, then in ViewData and after this use the value parameter given into helper method.
The 2 other places are in your case.
ModelState state = this.ViewData.ModelState["Version"];
state.Value; // this is the value out of the ModelStateDictionary
object value = this.ViewData["Version"]; // this is the value if set
// out of the ViewData Collection
The ModelStateDictionary gets its entries, while model binding. If you have the Version as a action method parameter, the Modelbinder (in your case the DefaultModelBinder) will enter the key version with the supplied value of get or post request.
If you change the value, put it in your model, you have to update the ModelStateDictionary too.

Form Submits same checkbox values on subsequent submit action in MVC

I have followed the suggestion in this question...
[How to handle checkboxes in ASP.NET MVC forms?
...to setup multiple checkboxes with the same name="..." attribute and the form behaves as expected the FIRST time its submitted. Subsequent submissions of the form use the original array of Guid values instead of properly sending the new array of checked item values.
Relevant code in the view...
<% foreach (ItemType itemType in ViewData.Model.ItemTypes) %>
<%{ %>
<li>
<input id="selectedItems" name="selectedItems" type="checkbox" value="<%= itemType.Id%>" />
<%= itemType.Description %></li>
<%} %>
This produces a series of checkboxes, one each for each item with the value="..." attribute set to the Id of the item.
Then in my controller action, the method signature is...
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult SelectItems(Guid[] selectedItems)
{...}
The first time thru the method, the selectedItems array properly holds the Guid of each item selected. But subsequent submits of the form will always still contain whatever was first selected in the initial submit action, no matter what changes you make to what it checked before you submit the form. This doesn't seem to have anything to do with my code, as inspecting the selectedItems array that the MVC framework passes to the method evidences that the framework seems to always be submitting the same value over and over again.
Close browser, start again, selecet different initial checkbox on submit and the process starts all over again (the initially-selected checkbox ids are always what's in the selectedItems argument).
Assume I must be thick and overlooking some kind of caching of form values by the framework, but I would swear this didn't behave this way in Preview 5.
Driving me nuts and probably simple issue; any ideas????
FWIW, here is what I do (not sure if it related):
// please MS, stop screwing around!!!!!!!!!!!!!!!
string r = Request.Form["r"];
Then proceed to extract the values manually from 'r'. I still use Preview 4, as they have really broken too many existing features, and not fixed reported bugs.
I'm not sure what is causing your issue, but I have a WAG...
Do you RedirectToAction in your controller's Post method?
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult SelectItems(Guid[] selectedItems)
{
/* lol snip */
return RedirectToAction("WhateverActionIsTheGetVersionOfThisPostAction");
}
It might serve to reset anything going on in the background... Again, wild-ass guess...

Resources