ASP.NET MVC using UpdateModel to post child records - asp.net-mvc

Continuing from this question, I've got a form with all the Vehicles listed for a Person, the vehicle fields are editable, and they are now successfully posting back to my Save action.
Now I'd like to use UpdateModel to save the data, but I'm not sure how to construct it. Here's my Save action right now:
<ActionName("Edit"), AcceptVerbs(HttpVerbs.Post)> _
Function Save(ByVal form As Person, ByVal vehicles() As Vehicle) As ActionResult
Dim original = From v In MyDataContext.Vehicles Where v.person_id = Person.person_id
For Each item In original
For i = 0 To vehicles.Count - 1
If vehicles(i).vehicle_id = item.vehicle_id Then
UpdateModel(item, New String() {"license_nbr", "color"})
Exit For
End If
Next
Next
MyDataContext.SubmitChanges()
Return RedirectToAction("Index", "Home")
End Function
When I run this, it doesn't save anything, and UpdateModel doesn't throw any errors. I'm assuming I have to give it a little more direction to get the magic to work, because UpdateModel doesn't know which item in the vehicles array to use for each update.
Do I need to specify a ValueProviderResult as the third parameter to UpdateModel? If so, how do I create one out of vehicles(i)? Am I completely off base in how I have this set up?

Why use UpdateModel -- which just updates the properties from form fields -- when you already have the form fields processed into model data? Can't you just assign the values from vehicle to item directly?
For Each item In original
For i = 0 To vehicles.Count - 1
If vehicles(i).vehicle_id = item.vehicle_id Then
item.license_nbr = vehicles(i).license_nbr
item.color = vehicles(i).color
Exit For
End If
Next
Next

Related

MVC4 EF cannot get saved value to display in dropdown on page load

I have never asked a question on StackOverflow before, and never wanted to, but I am desperate, so here we go: I cannot get a saved value to show up as the default value/display in a dropdown.
I set up the list in my controller:
public ActionResult Index()
{
//User Dropdown List
var users = Roles.GetUsersInRole("Manager");
SelectList list = new SelectList(users);
ViewBag.Users = list;
return View();
}
Then in the view an admin can then select one of these users and save it to my database via EF:
#Html.DropDownList("Users", ViewBag.Users as SelectList, "--Select Manager--")
This all works great, however, when you edit this entry, I want the dropdown list to show the current saved manager, not the first name in the list. I was hoping on my edit action that I could pull the current manager out of the database and pass it back into the dropdown as the default selected item, but no go:
public ActionResult Edit(int id = 0)
{
var theOwner = (from v in _db.Location where v.LocationID == id select v.Owner).FirstOrDefault();
var users = Roles.GetUsersInRole("Manager");
SelectList list = new SelectList(users, theOwner);
ViewBag.Users = list;
From all the examples I have read over the last 2 weeks, everyone has had 3 different values to work within their dropdowns, making it possible to use all the overloads in the SelectList method. However, my problem is that I just have this string list with only one item in it, so I can't utilize the overloads as I want.
So does anyone have an idea on how I can get this to work? Thanks a lot in advance for your time on this!
I'm pretty sure that if you modify the second parameter on the line where you create your SelectList, it should work -- it does for me.
Here is what I think the trouble is: Currently you are specifying the second parameter as 'theOwner', which is an object reference from the earlier Linq statement. But the SelectList contains a bunch of strings (the UserNames of the users which match the specified rolename). As a result, the SelectList doesn't 'know' how to match what you specified as the SelectedItem to something in the list of strings it contains.
But if you refine that second parameter so it specifies the USERNAME of the Owner that you just looked up, it should work. However I do not know what the correct property name is from your Location table. If the field you are currently selecting (v.Owner) contains the UserName itself rather than some Key then the syntax would be:
SelectList list = new SelectList(users, theOwner.Owner);
If that column actually contains a key for the User like an int or a Guid then you will have query for the UserName using the key, but the nature of the fix is the same.
Hope that helps.
A quick workaround is not to use #Html.DropDownList but plain html code.
As an example for your case, use the following html code in your View instead of Html.DropDownList helper:
<!-- NOTE: the ID and name attributes of "select" tag should be the same as
the name of the corresponding property in your Model in order for ASP.NET MVC
to edit your Model correctly! -->
<select id="User" name="User">
#foreach (var user in (SelectList)ViewBag.Users)
{
if (user == ViewBag.TheOwner)
{
<option value="#user" text="#user" selected = "selected" />
}
else
{
<option value="#user" text="#user" />
}
}
</select>
Also , for this to work you need to add one more line to your Edit method:
ViewBag.TheOwner = theOwner;
Another solution is also possible using #Html.DropDownListFor() however you haven't shown your model so I can't tell you what exactly to use. When DropDownListFor is used, ASP.NET MVC will select an option automatically based on the value in your model.

asp.net mvc reading html table/cell values in the controller

My application needs to do an HTTP post of a table with checkboxes like in the image above. On the controller side I will need to traverse the table and perform certain operations for each row that was checked.
The things that I need to do are:
Identify whether a row is checked
Get the cell values of a checked row
I have a good understanding on how this will be done in Razor in as far as posting the form is concerned. But I am clueless once I am in my controller's action method.
Please help. Thanks.
From what you've show, it appears that all you really need in your action method is a collection of ids to identify which "rows" to modify. I'd use a series of checkboxes with values set to the id of the row they represent. Presumably you have some sort of persistence mechanism in which these rows can be looked up or have them cached server side.
[HttpPost]
public ActionResult Update( List<int> rowIDs ) // where your checkboxes are named rowIDs
{
var messages = DB.Messages.Where( m => rowIDs.Contains( m.ID ) );
foreach (var message in messages)
{
// process the update
}
DB.SaveChanges();
return RedirectToAction( "index" ); // display the updated list
}
Note that it's more likely that you have a model with the collection of ids as well as some other data representing what "update" to perform. Posting collections can be tricky; you might need to play with the name of the input and/or with hidden indexes if you're not getting all the data posted back as expected.

ASP.Net MVC web application: Html.DisplayFor and Html.HiddenFor give different results

I'm implementing a two-step wizard-like process on a web page. The operation is reassigning a student to another teacher. Because there are many teachers across many sites, the first step is to choose from a drop-down list of sites. The second step is to choose a teacher at that site.
I have a single view model, StudentReassignmentForm, with properties necessary for each step of the process, like FirstName, LastName, StudentId, TeacherId, SiteId, etc. There's also a Step property, which keeps track of which step of the process is current. The post handler looks at the step number, and updates the form to show the next step, and then shows a view for that step. The same post method handles each step of the process. Stripped down to its core, my controller methods are:
public ActionResult ReassignStudent(int studentId)
{
// ... look up student and prepare for step 1 ...
StudentReassignmentForm form = new StudentReassignmentForm();
form.Step = 1;
form.StudentId = studentId;
form.FirstName = ...;
form.LastName = ...;
form.Sites = [IList<Site> of all sites]
return View("ReassignStudent1", form);
}
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult ReassignStudnet(StudentReassignmentForm form)
{
switch(form.Step)
{
case 1:
// Prepare for step 2 by looking up teachers at chosen site
form.Teachers = [IList<Teacher> of teachers at the site]
form.Step = 2;
return View("ReassignStudent2", form);
break;
case 2:
// Make final reassignment
// ...
}
}
The first step works fine. I can pick the site, and on the postback, form.SiteId is properly set.
The problem is that although I'm explicitly setting form.Step = 2, the view ReassignStudent2 is rendered with step value of 1. Uisng Fiddler, I see that what's going from the server to the browser is the value 1 (so it's not the view that's going wrong somehow).
EDIT: clarified problem in title
EDIT
I've done some more experimenting, and found that if I change my view to use Html.DisplayFor(m => m.Step) instead of Html.HiddenFor(m => m.Step), it renders the correct current step value of 2 instead of the old step value of 1. Then I tried Html.EditorFor(m => m.Step), and it produced the incorrect value of 1 also.
My POST handler uses the same model instance both as an input and as an output. The system creates a StudentReassignmentForm from the POST data, then I change some values, add some others, and use it to render the view for the next step. Is that a supported scenario? I wonder if the problem is related to caching the values of the lambda expressions (which I assume is done for performance). Maybe Html.HiddenFor and Html.EditorFor are picking up a stale cached value.
In any case, I see my workaround, which is just to hard-code the step number in my views.
Please try to use this statement immediately before returning a view in your controller.
ModelState.Clear();
I believe this should solve your issue.

Updating value provider prior to TryUpdateModel

Lets say we have a class with a property called PetsName. If it is left blank on the screen I want to update the value provider so if the user doesn't enter a pet name, we force 'unnamed'. This isn't the actual scenario.. this is of course a sample, so answers like 'just set default values on a webpage, etc' won't fit this scenario : )
The main issue is we want to update the values so when you update the model it will use whatever you have overridden. I guess one idea is to remove the value and add it. When I check ModelState, it does have the updated value, however when I call TryUpdateModel, the value is not updated. Its possible what Im doing below is indeed correct and there is another issue here but I figured I'd try this first. Thanks!
//Sample case:
[HttpPost]
public ActionResult Edit(PetOwner petOwner)
{
//If pets name is not set, force to "Unknown"
if(petOwner.PetsName=="")
{
//Tried this too ModelState.Remove("PetsName");
//ModelState.Add("PetsName", new ModelState());
ModelState["PetsName"].Value = new ValueProviderResult("Unnamed", "Unnamed", CultureInfo.CurrentCulture);
}
//Get the record/relationships from DB to merge with ModelState
PetOwner petOwnerToSave = from o in ctx.PetOwners where o.PetOwnerId == petOwner.PetOwnerId select o;
TryUpdateModel(petOwnerToSave);
//Save petOwnerToSave
}
The real issue behind the scenes here is that Html.HiddenFor wasn't displaying the correct value even though TryUpdateModel was updating a value in the model to give to the view.
The issue here is that the Html helpers assume if you are rendering a view after a post, there mustve been an error (otherwise you wouldve redirected back to the view with a GET method - hence the Post Redirect Get issue)
This is described in detail at:
http://blogs.msdn.com/b/simonince/archive/2010/05/05/asp-net-mvc-s-html-helpers-render-the-wrong-value.aspx

ASP.NET MVC Dropdownlist retain the selected value in the browser after post

I build a drop down list in my aspx page as following
<%= Html.DropDownList("SelectedRole", new SelectList((IEnumerable)Model.roles, "RoleId", "RoleName", Model.SelectedRole), "")%>
it works fine for first Get and the first default value is selected; then I select item from the drop down list and submit the form.
the controller bind the values correctly,
public ActionResult About([Bind] Roles r)
{
//r.SelectedRole = the selected value in the page.
//Roles r = new Roles();
r.roles = new List<Role>();
r.roles.Add(new Role(1, "one"));
r.roles.Add(new Role(2, "two"));
r.roles.Add(new Role(3, "three"));
r.roles.Add(new Role(4, "four"));
r.SelectedRole = null;
return View(r)
}
Then I nullify the selected item and return my view, but still the previous selected Item is selected (although I did nullify it)
Any Idea if I am doing something wrong or it is a bug in MVC?
I am using ASP.NET MVC 1
Thanks
This is the normal behavior of all html helpers: they will look at the POSTed values to perform the binding. This means that you cannot change the value in your controller action and expect it to reflect on the view if you use the standard helpers. If there's a value SelectedRole in the POST it will always be this value used and the last parameter of the drop down completely ignored.
You could write your own helper to achieve this or redirect after the POST.

Resources