Asked by:
View bug in preview 4 (worked fine in preview 3)

Question
-
User1102450110 posted
In preview 3 I was told that if I declare, for instance
Html.TextArea("mydata", ViewData["mydata"])
It would look first in the ViewData["mydata"] and if null, it would look in ViewData.Model.mydata and that was the behaviour, and worked fine.
Now in preview 4 the above declaration only looks for data in ViewData["mydata"] ignoring the Model. It's a big issue in CRUD because:
1) If I am creating, declaring ViewData.Model.mydata will throw a null reference exception
2) If i am updating, I want to get the Model data on first request, but the ViewData["mydata"] on following requests because there could be a wrong field elsewhere, and I should not lose the changes to the other fields on submitting (that's the purpouse of tempdata after all).
So it worked fine in preview 3 but is now broken. Any idea on how to work around it until it gets fixed?
Sunday, July 20, 2008 3:29 PM
All replies
-
User287763314 posted
Hello. well, I'm not sure if that was the case in preview 3, but if it was, then I prefer the current one. With the current one, you must pass the value that is applied to the textarea and this is the behavior that makes sense to me. If you're building a view, then you surelly know if you're using a strongly typed viewdata object or not. can you tell me why the behavior you describe was better than the current one? thanks.Sunday, July 20, 2008 5:49 PM -
User1102450110 posted
Well it was better for some obvious reasions. Let me explain with an example.
You have a view that is used both to create and update user data.
You have to structure your view so it allows to be rendered with no data at all (create user), with database populated data (update user) and temporary data (create or update user after an invalid post - like an invalid email address, but the other fields were valid).
If you can simply define that the input named "username" matches the "username" in the viewdata, everything works fine and simple. The ViewData loose collection takes priority, and if not present, the strongly typed model is checked.
In the actual structure instead, even if I assign data to the ViewData.Model object, it's useless, because I have to pass it again to the ViewData["myfield"] properties, since if I do in my view Html.TextArea(ViewData.Model.myfield) I have two problems: the first, in a create scenario, a nullreference exception is thrown; second, in both create and update scenario I would lose every user input if the post was unsucessful due to some invalid field. That whole thing makes the ViewData.Model property totally pointless, and I don't think it's been put there just to sit there and do nothing.
To prove that this is a bug, I can safely say that the html helpers that don't need the value property specified (like Html.TextBox) still work the old way: they check first for ViewData["inputname"] and if not present they check ViewData.Model.inputname . It's only the html helpers that need the value specified that are broken.
And here's another old post about that where I learned the correct behaviour: http://forums.asp.net/t/1279422.aspx
Sunday, July 20, 2008 6:30 PM -
User-1660457439 posted
As of Preview 4, the ViewData indexer is a normal dictionary indexer - it doesn't evaluate properties on the model object. Use ViewData.Eval() instead. (The reason that you're not seeing seeing this for all helpers is because the helpers that have gone through our full quality control - like TextBox - have been updated to call ViewData.Eval(name) rather than ViewData[name], so they should continue to work.)
Monday, July 21, 2008 1:27 AM -
User287763314 posted
Hello again. I have a different perspective here...I say that the view and the helpers shouldn't really do that. They should only receive the data they need to show. what I'm saying is that the controller should always be responsible for passing the *correct* data to the view, ie, you should have a specific view object (I prefer to use a strongly typed approach instead of the dictionary approach) which is filled by the controller with the correct values (which should cover inserts, updates deletes or whatever scenario you have). In fact, by putting that responsibility in the controller, you can really increment the testing range of your app...Monday, July 21, 2008 10:16 AM -
User-1660457439 posted
Luis - I don't follow. The controller is already responsible for populating the ViewData, and the view is responsible for consuming it.
Monday, July 21, 2008 12:38 PM -
User287763314 posted
Hello again guys. Maybe I'm not following then. In my 2 or 3 experiences with MVC, I always use a strongly typed view. So I always get the the data from the model property (ie, the values passed to control alwas come from the model property). Since I really haven't used any of the previous versions, can any of you guys tell me why it's so important to make the helpers trye to get a value from the dictionary syntax and then from the model property? I mean, with the current release, the helpers aren't doing any "guessing" and this seems fine to me, but maybe I'm wrong... maybe a specific example would help me understand why the previous behavior is better? thanks.Monday, July 21, 2008 2:12 PM -
User1102450110 posted
Luis, the reason is clearly explained in my second post. Read it carefully :)
Monday, July 21, 2008 2:43 PM -
User287763314 posted
Hello again. there's one thing I'm not getting: how are you filling the info that is being passed to the view? can you put some demo code for all the scenarios? After seeing it I'll try to explain what I'm saying... thanks!Monday, July 21, 2008 5:11 PM -
User1102450110 posted
One little thing before the example: in another post I got the solution. Simple use ViewData.Eval("MyField") to get the desired behaviour. Now for the example...
Here's a brief example using only 1 field. As for the view:
<%= Html.TextArea("MyText", ViewData["MyText"]) %>
In the controller,the actionmethod that is used to create a new record:
public ActionResult CreateNew()
{
return View("MyEditView");
}
the method for updating an existing record:
public ActionResult EditExisting(String id)
{
//loop through tempdata and assign any values from it to viewdata equivalents, so if we come here by an error in the SaveData method, we still have the user input
//get the item from LINQ to SQL using the specified ID
ViewData.Model = retreiveditem;
return View("MyEditView");
//note: in the perfect scenario, the view will render everything from ViewData.Model except for data present in ViewData dictionary, so user input (if we come from a SaveData error) takes priority on database data
}
and the actionmethod called with POST:
public ActionResult SaveData(String MyText)
{
//try to save the data, if ok return success view
//in case of error or invalid user input, memorize MyText in TempData["MyText"] and redirect to EditExisting method
}
This example should make it all clear. As stated above, the use of ViewData.Eval("varname") solves it, so I think further debate is pointless.
Monday, July 21, 2008 7:30 PM -
User287763314 posted
Hello again Matteo. Ok, so what you're telling me is that you might end up adding each property two times because you might add an object to the viewdata.model property and then you might have also add an entry to the viewdata dic whose key is the name of the property and in this case the value comes frmo the tempdata property. is this correct? if it is, then what I'm saying is that you could change your controller logic so that it only passes the object assign to the viewdata.model. what you must do is make sure that the object you're passing is correclty filled, which means that in some scenarios you'll get it from a db and in another you'll get it from tempdata. In practice this means that you'd probably star by checking the tempdata and if it's null you need to get the item from somewhere else (probably from the db). does this make sense?Tuesday, July 22, 2008 5:05 AM -
User1102450110 posted
It does, but this is exactly the logic of the ViewData.Eval method that solved the problem.Tuesday, July 22, 2008 6:04 AM -
User287763314 posted
Hello again. yes, I understand that. but when you do that, you're making your view a little more intelligent than it needs to be. or am I seeing it incorrectly?Tuesday, July 22, 2008 6:15 AM -
User1102450110 posted
It's arguable. Though the view is responsible only of showing received data, I'm not so sure that it is not its concern to display the correct data in an ambigous scenario like preserving user input on a failed post. That little "Eval" method saves me from passing each single value back and forth between ViewData dictionary and ViewData.Model, and saves me to write a specific action method to handle failed posts, so in my opinion it's a good thing.
Think about it: if we couldn't do that, we would have to pass evetytime all data to the dictionary, even on first request, making ViewData.Model totally useless. In fact, you couldn't use it in any way.
Tuesday, July 22, 2008 6:39 AM -
User287763314 posted
Hello again. I'm using something similar to the approach I've mentioned in my current app. It has increased logic in the controller in order to get the "correct object" that is passed to the viewdata.model property, but I hhaven't really had any problems with it til now... I'll try to reuse your approach to see how it feels like...Tuesday, July 22, 2008 6:46 AM -
Thursday, August 14, 2008 8:58 AM
-
User1220545395 posted
By the way, you can shorten that to this:
<%= Html.TextArea("MyText") %>
Thursday, August 14, 2008 10:49 PM -
User-1876899917 posted
By the way, you can shorten that to this:
<%= Html.TextArea("MyText") %>
Not in preview 4 - TextArea() doesn't have an overload that takes only one parameter.
Is this something being changed in the next release?
Friday, August 15, 2008 5:40 AM -
User1220545395 posted
Ah right, I believe that will change.Friday, August 15, 2008 11:24 AM