I have an MVC 5 / Bootstrap application. On one of the pages, I have a number of fields all bound to the model associated with the page. However, I also have a simple unordered list which always starts out empty and the user can then add items to it. They do this by entering some text into a type ahead field. Once the user finds what he/she is looking for, he/she can click a button and have it added to the unordered list. Any number of items can be added to the list. All of this works fine.
My question is how I can get the contents of the unordered list posted back to the server along with the rest of the form contents, since the unordered list isn't part of the model?
Here is one way to skin this cat:
A) Add a collection to your model (which really should be a ViewModel, and not a domain model) to hold those items
B) In your button's click handler, create a hidden input field that conforms to the ASP.Net Wire Format: http://www.hanselman.com/blog/ASPNETWireFormatForModelBindingToArraysListsCollectionsDictionaries.aspx
If you had a collection of orders, you should end up generating controls like this:
<input type="hidden" name="Orders[0].Id" value="1" />
<input type="hidden" name="Orders[1].Id" value="2" />
Note sequential ordering is important, if you start removing items, you'll need to re-sequence your name values.
There couple of ways to work it out.
If you don't want to add to model, what I would prefer to do you can:
Directly access item that were posted via Controller.Request property;
You can post this items separately via Ajax request, and handle them in different controller action.
I have a strongly typed view where one of the properties of the model that is passed in is a list of objects. I am handling dynamic binding of inputs to this list by using the BeginCollectionItem helper which works fine. However, I also want to initially have a group of inputs visible on the view that will bind to the 'first' object in the list.
I've tried just copying the inputs that BeginCollectionItem generates but that doesn't seem to work.
It generates inputs with name attributes like
invoiceItems[ef43a8f2-c6b7-4791-bf7f-6764c8e3fc9b].Description
invoiceItems[ef43a8f2-c6b7-4791-bf7f-6764c8e3fc9b].Cost
so I manually put 2 inputs on the view with the name attributes
invoiceItems[firstOne].Description
invoiceItems[firstOne].Cost
but it didn't show up in the model on the controller after submitting the form.
I can't just insert one the BeginCollectionItem way when the view loads because the initial item has to be displayed differently.
Not exactly answer to your question, but I hope it will solve your problem.
Instead of changing the name in the view, you may try changing the model. Add a boolean property IsFirst. I believe you know in the controller(or wherever the model is being instantiated) which one is first. Set the first items IsFirst to true.
I figured it out. I was on the right track with manually putting in the stuff that BeginCollectionItem did, I was just missing a part. You need another hidden input that tells the binding what the index you're using is.
So the following works perfectly
<input type="hidden" name="invoiceItems.index" autocomplete="off" value="firstOne" />
<input type="text" placeholder = "ex. Labour" id="invoiceItems_firstOne__Description" name="invoiceItems[firstOne].Description" />
<input type="text" placeholder = "ex. $15" id="invoiceItems_firstOne__Cost" name="invoiceItems[firstOne].Cost" />
I just added the top hidden input to what I had before and it's all good.
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
I can't seem to get the inputted text of a textarea. When I do :
die($request->getPostParameter('comment'))
It outputs the word "array". When I print_r() it does show the textarea is an array and its value is stored in the array. But I don't know how to get that value so I can put it into a field in a table.
#greg0ire: I am doing this because I am trying to save data to two different tables. My html page displays a form that is actually made up of two forms from two different classes/models. I have managed to save all fields to both tables except for the comment field. I then tried getting the value and realising it was an array, wondered if this was what was causing my data not to save. This is why I am asking this question. I have asked another question which explains the context.
These are the functions that run on clicking the submit button
public function executeUpdateInlineForm(sfWebRequest $request)
{
$overdueInvestigation = Doctrine_Core::getTable('investigation')->find( $request->getParameter('id'));
$investigationForm = new investigationInlineForm($overdueInvestigation);
$commentForm=new commentForm();
$investigationForm->bind($request->getParameter($investigationForm->getName()), $request->getFiles($investigationForm->getName()));
$commentForm->bind($request->getParameter($commentForm->getName()), $request->getFiles($commentForm->getName()));
$this->processInlineForm($investigationForm, $commentForm);
}
protected function processInlineForm(sfForm $investigationForm, sfForm $commentForm)
{
if ($investigationForm->isValid())
{
$investigation = $investigationForm->save();
$comment = $commentForm->updateObject();
$comment->setInvestigation_id($investigationForm->getObject()->getId());
$comment->setComment($commentForm->getObject()->getComment());
$comment->setuserId($investigationForm->getObject()->getCreatedUserId());
$comment->setDateEntered(time());
$comment->save();
$this->redirect('investigation/overdue/');
}
}
you could simply store $request->getPostParameter('comment') in an array and use array_pop() on this array, but I think it would be better to understand why you're getting an array. I think the name of the textarea must be comment[], when it should probably be just comment.
UPDATE
After reading your update and your other question it seems you need to have this naming convention for your fields:
<input type="text" name="investigation[field1]"/>
<input type="text" name="investigation[field2]"/>
<input type="text" name="investigation[field3]"/>
<input type="text" name="comment[content]"/>
Use the setNameFormat() method on the widget schema of your forms to achieve this, then bind your investigation form to the investigation request parameter, and your comment form to the comment parameter and you will be fine.
Good luck!
I have seen lots of questions relating to this topic.
I am using asp.net MVC 1.0
Problem area
If I use
<%= Html.CheckBox("Status", true) %>
Then why It renders like
<input checked="checked" id="Status" name="Status" type="checkbox" value="true" /><input name="Status" type="hidden" value="false" />
I put this in foreach loop and I have 5 rows.
when I submit form with true,true,true,false,false
then I get true,false,true,false,true,false,false,false
i.e. for false => false.
for true => true,false
If I use
<input type="checkbox" value="true" name="Status" checked="checked" />
Then I don't get unchecked one's.
so how do I overcome form this problem?
Please don't post answer with using loop in formcollection object and checking each key!
I know this isn't the elegant one but this is what I did:
collection["response"].Replace("true,false", "true").Split(',').ToList();
In your example, when you submit form with true,true,true,false,false and you get
true,false,true,false,true,false,false,falseit is interesting to note that you are not actually getting eight values back, but five arrays that merely looks like this is the case because all of the values are joined.
I know you asked to not get a loop for your answer, but I can use one to demonstrate what is really happening here:
foreach (string key in postedForm.AllKeys) {
// "value" will contain a joined/comma-separated list of ALL values,
// including something like "true,false" for a checked checkbox.
string value = postedForm[key].GetValue;
// "values" will be an array, where you can access its first element,
// e.g., values[0], to get the actual intended value.
string[] values = postedForm.GetValues(key);
}
So, for your checked boxes, you'll get a values array with two elements, and for unchecked boxes, you'll get just a single-element array.
Thus, to answer your question how do you overcome this problem, the answer lies in using GetValues rather than GetValue, and thinking of your posted fields as arrays rather than strings.
Best of luck!
Personally I think having to check for "true,false" everywhere on the server is a pain. I wrote a jquery fix that will remove the extra hidden field created by the Html.Checkbox helper when a box is checked, then add it back if the box is unchecked. Server values will always be "true" or "false". Checkbox lists are kind of subjective in how you want them to act, which I discuss, but I'm removing "false" from the value set, which means the form value will be excluded if all boxes in the list are unchecked.
http://www.mindstorminteractive.com/blog/?p=386
I've had pretty good success using this technique. Please let me know if you try it out and have issues.
You'll have to do your own model binding for the CheckBox values.
Get the list of values from the FormCollection or Request.Form for that CheckBox id and replace true,false with true:
string chkBoxString = Request.Form["checkboxID"].Replace("true,false", "true")
Now you have a list of whether a CheckBox was selected or not.... do the rest yourself :)
It renders so because default binder requires the FormCollection to have a value for nonnullable parameters. Using this technique we are sure that the value will be sent even the checkbox is not checked (by default the value sent only when it's checked). If you use this controller method with just one html input you'll get error on form post with unchecked checkbox (value of checkbox will not be posted and binder will not know what to use for value of isItemSelected):
public ActionResult SomeActionMethod(bool isItemSelected)
You can try use something like this with just one html input:
public ActionResult SomeActionMethod(bool? isItemSelected)
But in this case isItemSelected will be null or will be true. And it will never become false.
Well there are couple of ways you can do based on your requirement.
I use this method.
<input type="checkbox" value="<%= item.ID %>" name="check" checked="checked")" />
This is may checkboxes.
On server side I will also have array of ID's of item in the model.
So I check it whether it is in array
var strArray = form["checkbox"]; //Request.form["checkbox"] or "FormCollection form" in action parameter; array of ID's in comma separated string.
Different people have different tests.
this was intended to use for just just simple CheckBox, what you want is checkboxList, which is not yet cover in the API of ASP.net MVC
If you looking for some thing like checkboxlist, maybe you should write your own helper, provide you understand HTML well..
That's it! :)
Easier just to check whether AttemptedValue.Contains("true") - it will, if it's checked, not if it's unchecked....
in the View :
<input id="radio5" type="checkbox" name="rb_abc" value="5"/>
Controller:
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult YourForm(FormCollection fc)
{
if (fc["rb_abc"] == "on")
{
//Hey .. you have selected a Radio Button.. just kidding.. its a CheckBox
}
}
To get checkbox value even it is true or false
var boolValue = bool.Parse(collection.GetValues("checkboxID")[0])