How to bind list of dynamic objects to the Model - asp.net-mvc

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.

Related

MVC 5 - Returning an unordered list to the server, along with a form

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.

Dynamic Form in ASP.NET MVC3 Razor

I have a small problem.
I want to have dropdown list with some objects. After clicking on one I want to add it to list with textfield for naming it. I don't want to limit quantity of this fields. I want to receive in controller ID (stored in dropdown list) and name (given by user) for each selected item. How can I do it?
I was thinking about storing it in some fields as a text, and parsing in cotroller but I think it's not elegant.
EDIT.
Ok, Thansk for your help, but it's not working for me correctly.
I generate html like this:
<input type="hidden" value="96" name="Inputs[0].Key">
<input type="text" name="Inputs[0].Value">
In my controller I'm receiving this dictionary. The problem is that quantity of elements is correct, but all values are null. What is wrong here?
The best way to go about this is by using array-style model binding.
So, for each element you wish to name you create a hidden field to store the drop down value plus a text field to store the user-given name. You name them as follows:
<input type="hidden" name="element[0].Key" /><input type="text" name="name[0].Value" />
increasing the index value each time. This is easily achieved with a bit of JavaScript. You then create an action method which takes a KeyValuePair<string, string>[] as a parameter. You will then be able to parse through your values no problem with a loop or LINQ expression.
Use IEnumerable<KeyPairValue<string,string>> MySelectedItem = new List<KeyPairValue<string,string>>(); on model, and when adding it to the list, name it like an array:
MySelectedItem[1].Key, MySelectedItem[1].Value, MySelectedItem[2].Key...
(I haven't tested this, but it should work)
Edit: check out this blog post with better explanation on how to do it: http://haacked.com/archive/2008/10/23/model-binding-to-a-list.aspx

Binding checkboxes to array with Knockout.js (MVC Razor)

I have a Knockout.js view model that looks like this:
"UserName":null,
"FirstName":null,
"LastName":null,
"Countries":{
"arrCountries":[]
}
And a set of country checkboxes that all must be rendered with the same data-bind value (I'm using this custom CheckBoxListFor helper, which can apply custom HTML to each checkbox, but it's the same custom HTML for each checkbox. It takes a property that lists every available country from my razor view model to create the checkboxes, and then renders some as marked based on another razor view model property).
The checkbox values are integers (1, 2, 3, etc) and it's those integers that I'd like to throw into the arrCountries property of the view model.
Here's how a checkbox is rendered as it stands at the moment:
<input checked="checked" data_bind="checked: ???" id="Countries.arrCountries107" name="Countries.arrCountries" type="checkbox" value="1"></input>
I've proven the concept of using viewModel.Countries.arrCountries.push(1); to update the view model (followed by an alert that gives me a count of the elements in arrCountries to prove that the push worked), but I can't seem to get the data-bind HTML property on the checkboxes to wire up so that they check and uncheck as I send push and remove commands.
Any help much appreciated!
Make the checked data attribute point to the selected countries. If the checkbox's value is in the array of checkboxes, then it will be checked. It won't otherwise.
<input checked="checked" data-bind="checked: Countries" type="checkbox" value="3"></input>
Take a look at this fiddle I put up.
http://jsfiddle.net/u8xP9/3/

How to mimic MVC's checkbox -> bool model binding?

I've got an editor template which renders out a checkbox:
#Html.CheckBoxFor(model => model.Follow)
Which renders something like this:
<input checked="checked" data-val="true" data-val-required="The Follow field is required." id="Follow" name="Follow" type="checkbox" value="true" />
<input name="Follow" type="hidden" value="false" />
AFAIK the hidden field is something to do with catering when an unchecked box isn't sent to the server or something.
Anyway, if i take a look at the Request.Form["Follow"] when the checkbox is checked, i see a value of "true,false".
How do i coerce a bool from this value? Do i simply ignore the second field? (e.g the hidden field).
I'm doing this is a base controller (protected method, invoked from child controller), so i don't have a strongly-typed view model, only the raw Request object.
Can anyone help? Or alternatively, if someone could point me to where in the MVC source code this happens, i could take a look myself, but not sure where to start looking.
You are correct the hidden field is just so the form will be submitted to the server. Because if the form had just checkboxes that are not checked then nothing will be submitted and the server would not know to set them to false.
You only require 1 hidden field per form, you do not need one per checkbox. But if your making your own control it is hard to tell if a hidden textbox is already on the field or not. If you know you are always going to have a textbox or select list etc somewhere else on your forms you do not need a hidden textbox at all
You can rename your hidden textbox to anything name it "dummy" or something different to the checkbox name so Request.Form["Follow"]; will only return the value of the check box not need to split. You never need to check the value of the "hidden textbox".
On a side note you shouldn't be using Request.Form["Follow"] you Action method should have a parameter like this instead "bool? follow"
MVC helper renders checkbox input control with two input fields, the checkbox and the hidden, because the browser do not send a value for checkbox input field if the checkbox is not selected. If you do not use auto mapping, you need to parse the input value that you receve from your form.
Use this simple rule to detect the checkbox:
var rawFollow = Request.Form["Follow"];
if (rawFollow.Contains("true"))
{
// do something
}
As far as i know, the extra hidden field is because if the checkbox is NOT checked, that input will not be submitted with the form and therefore we need the hidden field with the value of false.
So the only solution is can think of is this:
var rawFollow = Request.Form["Follow"];
var rawFollows = rawFollow.Split(',');
if (rawFollows.Count() > 1)
{
rawFollow = rawFollows[0];
}
But this seems hacky (and what about the order of the elements on the page, what if for some reason the hidden field was FIRST, then it would always evaluate to false), which is why i'm wondering how the MVC source does this.

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

Resources