So I have an action method like this:
public ViewResult CareerSearch()
{
CareerSearchModel model = GetCareerSearchModel();
return View("Search", model);
}
In the view, I manually create a list of checkboxes from one of the model properties. The output of that ends up looking something like this:
<input id="location51438342" type="checkbox" checked="True" value="2" name="locations">
<label for="location51438342">Austin</label>
<input id="location14609737" type="checkbox" checked="True" value="9" name="locations">
<label for="location14609737">Dallas</label>
<input id="location25198218" type="checkbox" checked="True" value="11" name="locations">
<label for="location25198218">Houston</label>
So, on the action method that handles the form POST, I want to get a reference to the model AND the checkbox integer array. However, when I step through the following action method, "model" is null:
[HttpPost]
public ViewResult CareerSearch(CareerSearchModel model, int[] locations)
{
//omitted for brevity
}
What am I missing here? How do I get the reference to my model AND the array of checkbox values?
you'd have to be posting back all the fields of the model itself too aka have them in hiddenfor labels inside of the form. you can use firebug to see what is posted back. if it's not being posted back you'd have to recreate it in your action after the post.
The model is not saved anywhere on the server and is not automatically posted back in the POST data (use Fiddler to confirm). You must instantiate the model the same way you do in the GET action and use the other parameters to modify it accordingly.
See NerdDinner on how to map POST form data into your model.
Related
I have a partialview that has a viewmodel containing a form with only email and name input. I post this data with jquery ajax to my controller to do some validation. My controller is a PartialViewResult method, see:
[HttpPost]
public PartialViewResult InviteUser(InviteEmailViewModel item)
{
return PartialView("_InvitedUsers", item);
}
My jquery adds the result of this method to the DOM, since ive uses hiddenfor i get inputs like:
<input data-val="true" data-val-required="Name is required." id="Name" name="Name" type="hidden" value="test">
<input data-val="true" data-val-regex="Invalid Email Address" data-val-regex-pattern="^(([A-Za-z0-9]+_+)|([A-Za-z0-9]+\-+)|([A-Za-z0-9]+\.+)|([A-Za-z0-9]+\++))*[A-Za-z0-9]+#((\w+\-+)|(\w+\.))*\w{1,63}\.[a-zA-Z]{2,6}$" data-val-required="Email Address is required." id="Email" name="Email" type="hidden" value="test#test.test">
The problem with this is that my ViewModel contains a List but after posting the form with the added partialviews the List is Null. Ive tested with hardcoded input fields like:
<input data-val="true" data-val-required="Name is required." id="Name" name="InviteEmailViewModel[0].Name" type="hidden" value="test">
and that does seem to work. How do i make MVC generate hidden fields like in my hardcoded example?
To answer your Q:
To include hidden fields in the post, these need to 'survive' the create-in-controller-survive-the-post, if the field isn't in the form, then it won't be posted back to the controller, regardless if it's been created before in the controller.
Add the hidden field anywhere within the form so that it's posted back. So when it's created it'll have a container and will therefor be present on post.
IE:
#Html.HiddenFor(m => m.myHiddenField)
#Html.TextBoxFor(m => m.displayedField, new { #class = "NotHidden" })
What it could be:
If I'm understanding correctly you've got a ViewModel with a List, which is the model of the partial, which you want to add dynamically to your VM and bind on post.
To add items to a list dynamically, without them going null, you'll need to use BeginCollectionItem, which handles the hidden GUID issue for you.
I had similar issues and asked two questions which have solutions here and here, the latter is a problem with accessing a property two models down, but shows more complete code.
The issues that you've got are with the GUID. It doesn't go back through the post without help.
What you need is a prefix in the name/id so that the object can be properly assigned to its model when you post.
BeginCollectionItem will sort this out for you - which you can get 'stock' via typing Install-Package BeginCollectionItem in Package Manager Console in Visual Studio, check out this guide - here or you can download the file/whole thing and add it to your project manually by going to GitHub here
name="viewmodel[f7d8d024-5bb6-451d-87e3-fd3e3b8c1bba].listOfObjects[d5c08a43-f65e-46d1-b224-148225599edc].objectProperty" is what you need on the dynamically created model properties so that they all bind on post back.
Good luck. =)
So I am trying to set up a website that has questions, answers, and comments, much like stackoverflow. right now, I am trying to get comments to work correctly. I have decided that I will try to use an ActionLink to send the comment text to the controller. Is there any way that you can do this without invoking the model fields?
Use a simple form submit, then grab your submitted data via a FormCollection
[HttpPost]
public ActionResult SubmitComment(FormCollection collection)
{
string comment = collection["comment"];
}
and for your view
#using (Html.BeginForm("SubmitComment", "CommentsController"))
{
#Html.TextBox("comment")
<input type="submit" value="submit" />
}
In my opninon you are better off binding this data to a model in the long run. See this link for more details : Is there any good reason to use FormCollection instead of ViewModel?
What if you place your inputs to <form> tag ?
#using (Html.BeginForm("AddComent", "CommentsController"))
{
#Html.TextBox("comment")
<input type="submit" value="Post Your Comment" />
}
P.S.
Other option (that i prefer) would be to send Ajax Post calls with JQuery: $.post("#Url.Action("$Action", "$Controller")", { $parameter: $data });
I have the following in my controller:
ViewBag.UserList = userlist.ToList();
return View ();
The userlist have the following 3 fields:
First Name, Last Name, Phone Number
How do I go about creating a list in the view where the user can modify the fields?
I am well aware of using the foreach to loop through the list but how do I program the ability to modify the records?
I also need to know how I can then send this list back to the controller so that I can insert the records into my database.
As you said, iterate the list in the view, Use the TextboxFor() method of each property you want to modify, add a submit button, in the controller action add List<T> list to catch the changes and there you go.
I'll assume this is a web mvc project since it's tagged asp.net-mvc.
the view will render html. the html will contain (at a minimum) a form, inputs for each editable value and a submit button. ultimately the markup will look like this
<form id="?" method="post" action="usercontroller/update">
<input name="users.firstname[0]" value="john"/>
<input name="users.lastname[0]" value="doe"/>
<input name="users.phonenumber[0]" value="555-123-4567"/>
<input name="users.firstname[1]" value="jane"/>
<input name="users.lastname[1]" value="smith"/>
<input name="users.phonenumber[1]" value="555-123-4567"/>
<submit value="update"/>
</form>
on the server you would have a controller
class UserController : BaseClassIfNecessary
{
public ActionResult() Update(User[] users)
{
foreach user update database
return RedirectAction("url");
}
}
I need to make a view which will support bulk inserting of objects. I am using the repository pattern with Entity Framework 4.
Since there will be multiple individual models in the View, how do I handle binding the view's model to what get's sent to the Repository?
How do I retreive the input values from the form into the controller?
I have managed to do the view but I am unable to retrieve the data from controller. I used the folowing statement but after the post it heppens to be nul
public ActionResult AddPaymentForRoot_post(PaymentViewModelObject payments) {
return null;
}
I haven't used EF yet, but I have a similar requirement on our site. I accomplished it by specifying the View as inheriting System.Web.Mvc.ViewPage<IEnumerable<MyModelObject>> then use a for loop to build multiple input sections, utilizing the naming conventions required to allow the model binder to build the enumerable.
I prefer to create a view model with a property that is an enumerable (see update 2 below). In this example we could say it would be the MyViewModel object with an IEnumerable<MyModelObject> property called MyModelObjects. In this case, the view would play out as follows...
Specifically, make sure to include for each input section a hidden field named MyModelObjects.Index, then have your property inputs named MyModelObjects[0].MyProperty, where the value of the Index hidden field corresponds to the array index in the property names.
What you would end up with would look like this, for example:
<div>
<input type="hidden" name="MyModelObjects.Index" value="0" />
Name: <input type="text" name="MyModelObjects[0].Name" /><br />
Value: <input type="text" name="MyModelObjects[0].Value" />
</div>
<div>
<input type="hidden" name="MyModelObjects.Index" value="1" />
Name: <input type="text" name="MyModelObjects[1].Name" /><br />
Value: <input type="text" name="MyModelObjects[1].Value" />
</div>
<div>Add another item</div>
In some cases I also have javascript (triggered by the anchor link in the markup above) that will clone a template input section, modify the names to populate the index value, and add it to the DOM. In this way users can dynamically add records to the view.
Depending on how you are handling your models, you may also pass in a ViewModel that has a property that is IEnumerable. The typing for the view would change, but fundamentally the design of the view wouldn't.
UPDATE
Using the example above, here's a quick snippet of the jQuery code I use to add items. I'm assuming this is an insert, but note the comments if you wanted to support this type of interface for editing as well.
First, I set up a template. While this could be built totally in jQuery, I just find it easier to do the markup and manipulate the names.
<div id="templateDiv" style="display: none;">
<input type="hidden" name="MyModelObjects.Index" value="0T" />
Name: <input type="text" name="MyModelObjects[0T].Name" /><br />
Value: <input type="text" name="MyModelObjects[0T].Value" />
</div>
You can also specify IDs in there...probably a good practice, but in the interest of brevity I'll leave it out for the example.
With that, you can set up the jQuery function as follows:
var currentIndex = 1 // if you were using this for editing, this would be dynamically set to the number of items you have
var doAddItem =
function() {
currentIndex++;
var newItem = $("#templateDiv").clone();
newItem.attr("id", "item" + currentIndex); // reset the ID here
newItem.find("input[name=MyModelObjects.Index]").val(currentIndex);
newItem.find("input[name$=.Name]").attr("name", "MyModelObjects[" + currentIndex + "].Name");
newItem.find("input[name$=.Value]").attr("name", "MyModelObjects[" + currentIndex + "].Value");
newItem.insertBefore($(this).closest("div")); // insert it before the div containing the add link...adjust appropriate to your layout
return false;
}
$(document).ready(function() {
$("#addItem").click(doAddItem);
// In a real-world app you'd probably want to have a delete option, too
});
UPDATE 2
There's a bit of an error up there - if your view is typed to an IEnumerable<object> then your field names will end up just being Index and [0].Name / [0].Value. In my actual app I use a view model that has a property that is itself an enumerable, so I actually type my pages as <MyViewModelObject>. In the example above, that view model object would have a property called MyModelObject, which would be an enumerable (and more realistically called MyModelObjects plural).
Something pretty weird is happening with my app:
I have the following property in my ViewModel:
public int? StakeholderId { get; set; }
It gets rendered in a partial view as follows:
<%= Html.Hidden("StakeholderId", Model.StakeholderId) %>
The form is submitted, and the relevant controller action generates an id and updates the model, before returning the same view with the updated model
The problem I'm experiencing is that the hidden field does not have anything in its "value" attribute rendered the second time even though StakeholderId now has a value.
If I just output the value on its own, it shows up on the page, so I've got it to render the value by doing this:
<input type="hidden" id="StakeholderId" name="stakeholderId" value="<%: Model.StakeholderId %>" />
But it's pretty strange that the helper doesn't pick up the updated value?
(I'm using jQuery to submit forms and render the action results into divs, but I've checked and the html I get back is already wrong before jQuery does anything with it, so I don't think that has much to do with anything)
UPDATE
I've since discovered that I can also clear the relevant ModelState key before my controller action returns the partial view.
The helper will first look for POSTed values and use them. As you are posting the form it will pick up the old value of the ID. Your workaround is correct.
ADDENDUM: Multiple HTML Forms, eg, in a Grid
As an addendeum to this issue, one thing to be VERY careful of is with multiple forms on the same page, eg, in a grid, say one generated using Ajax.BeginForm.
You might be tempted to write something along the lines of:
#foreach (var username in Model.TutorUserNames)
{
<tr>
<td>
#Html.ActionLink(username, MVC.Admin.TutorEditor.Details(username))
</td>
<td>
#using (Ajax.BeginForm("DeleteTutor", "Members",
new AjaxOptions
{
UpdateTargetId = "AdminBlock",
OnBegin = "isValidPleaseWait",
LoadingElementId = "PleaseWait"
},
new { name = "DeleteTutorForm", id = "DeleteTutorForm" }))
{
<input type="submit" value="Delete" />
#Html.Hidden("TutorName", username)
}
</td>
</tr>
}
The lethal line in here is:
#Html.Hidden("TutorName", username)
... and intend to use TutorName as your action's parameter. EG:
public virtual ActionResult DeleteTutor(string TutorName){...}
If you do this, the nasty surprise you are in for is that Html.Hidden("TutorName", username) will, as Darin Dimitrov explains, render the last POSTed value. Ie, regardless of your loop, ALL the items will be rendered with the TutorName of the last deleted Tutor!
The word around, in Razor syntax is to replace the #Html.Hidden call with an explicit input tag:
<input type="hidden" id="TutorName" name="TutorName" value='#username' />
This works as expected.
Ie:
NEVER, EVER USE Html.Hidden TO PASS A PARAMETER BACK TO YOUR ACTIONS WHEN YOU ARE USING MULTIPLE FORMS IN A GRID!!!
Final Caveat:
When constructing your hidden input tag, you need to include both name and id, set to the same value, otherwise, at the time of writing (Feb 2011) it won't work properly. Certainly not in Google Chrome. All you get is a null parameter returned if you only have an id and no name attribute.