MVC Get and Post data - asp.net-mvc

Sorry for the really nooby question, but this is tripping me up hard.
I have a controller Inspection, with method Configure which passes a model containing 3 lists.
I know that I'm supposed to use HiddenFor to retain the information, but I don't know how to do this with lists.
I have these HiddenFor fields but they don't seem to work in retaining the information
#Html.HiddenFor(model => Model.inspection)
#Html.HiddenFor(model => Model.assignedParts)
for (int i = 0; i < Model.ConfigList.Count; i++)
{
#Html.HiddenFor(model => Model.ConfigList[i])
}

Every Razor directive gets converted into html to display in the browser. In this case, your directive for Model.assignedParts will probably look something like this in html:
<input type = "text" value="List<StevenHubs.Models.Part>" />
You can see exactly what it is by running your app and viewing the source. The text that is getting assigned to value is just Model.assignedParts.ToString(). The simplest way to fix your problem is to change your view to just use the pieces of the model that are actually getting changed.
If you want to include the list in your View, you will need to do a foreach over the list, and if the member of the list has properties, you will have to have a #Html.HiddenFor for each property so something like:
#foreach(var part in Model.Part) {
#Html.HiddenFor(part.Property1)
#Html.HiddenFor(part.Property2)
}

Related

Passing collection as model without ordered index

I have list of objects and I'm passing it to view and it is rendered properly.
When I submit this form, I'm getting getting the same model. Everything works fine.
Unfortunately, when I decide to delete dynamically some record using jquery, so it looks like this
After submitting form, I'm getting only list with 2 first items. It's probably, because indexes arent in a natural order (0,1,3 instead of 0,1,2).
Is there anything I could do to fix it easily (not using jquery to change inputs, smth server sided)? I've tried to change array to List or Ienumerable but still nothing. I know I could pack everything up and send as json or just read the formCollection, but I'd like to ask here first and see if there is some other solution.
You need to include an input for the Index property which allows you to post back non consecutive indexers. The value of the index must match the collection indexer. For example
for(int i = 0; i < model.tagList.Count; i++)
{
#Html.TextBoxFor(m => m.tagList[i].Name);
<input type="hidden" name="tagList.Index" value="#i" />
}

How do you set the initial value of a EnumDropDownListFor built using helper extension?

I followed a tutorial for MVC5 about coding a helper extension to populate dropdownlists using enums. This works perfectly until using an edit view.
On a create page I fill the ddl and select no problem. But when I want to update the value on an edit view, using a ddl filled just as on the create, I can't default the actual value (from the database) as the displayed value.
I've searched high and low but starting to think it can't be done. I'm not sure what code will help so here goes;
My ddl on the edit view
#Html.EditorFor(model => model.ProjectStatus)
ProjectStatus is the enum that is used to retrieve the enum value in the helper extension and populate the ddl.
So my ddl may contain statuses such as:
New,
WIP,
Rejected,
Fixed,
Closed
and my current value may = New. So when I load the edit view I would like the value New to be defaulted in to the ddl, and be able to change this value by selecting another using the ddl.
The value I would like to default to is actually in the view (model.Status). I just can't work out how to include this in the code above.
I hope this makes sense and any help appreciated.
Thanks for reading.
I'm not exactly sure what your extension method looks like. If you want a more direct answer, you'll need to include more details on your specific implementation. That being said, here is some sample code from an edit.cshtml template on Autoquoter.com
<div class="control-group">
#Html.LabelFor(model => model.AddonType)
<div class="controls">
#Html.DropDownListFor(model => model.AddonType, EnumHelper.SelectListFor((AddonType)Model.AddonType))
#Html.ValidationMessageFor(model => model.AddonType)
</div>
</div>
Notice the parameter to EnumHelper.SelectListFor. We pass in the current value to the helper method. Then we add it as the final parameter to the SelectList constructor.
public static SelectList SelectListFor<T>(T selected) where T : struct
{
Type t = typeof(T);
return !t.IsEnum ? null
: new SelectList(BuildSelectListItems<T>(), "Value", "Text", selected.ToString());
}
For anyone that may be interested I fixed this using jquery.
I was getting hung up on drop downs filled by enums, razor and html helpers etc. I looked at the raw html produced and approached it from that angle.
The following code works for me but I'm no expert so there may well be better solutions
// set initial value of Status ddl
$(document).ready(function () {
var initialValue = $("##Html.IdFor(model => model.Status)").val();
$("select option").filter(function () {
return $(this).text() == initialValue;
}).prop("selected", true);
});
I hope this helps.

EditorFor not rendering enumerable without #foreach

This is rather baffling. Imagine a Company entity with an .Employees enumerable property, for the sake of simplicity. I've seen elsewhere on SO that the trick to getting EditorTemplates to render enumerables sensibly is to use syntax like this:
In Index.cshtml:
#Html.EditorFor(company => company.Employees, "EmployeeEditorTemplate")
EmployeeEditorTemplate.cshtml expects a single employee:
#model MyNamespace.Models.Employee
//...
#Html.EditorFor(x => x.FullName)
// ...etc.
Posts like this one: Viewmodels List Is Null in Action ...indicate that the modelbinder is smart enough to figure out the binding and index the enumerable on the fly. I'm getting different results (exactly what I'd expect if the modelbinder wasn't supposed to be so mageriffic):
The model item passed into the dictionary is of type
System.Collections.Generic.List`1[MyNamespace.Models.Employee]',
but this dictionary requires a model item of type
'MyNamespace.Models.Employee'.
Enumerating it myself renders the collection just fine, but emits the same id for every row's controls (which is less than optimal, of course, but it tells me the EditorTemplate is functionally correct):
#foreach (var item in Model.Employees)
{
#Html.EditorFor(x => item, "EmployeeEditorTemplate")
}
This construct also fails to post the company.Employees collection back to my other controller action.
Any ideas on what's required to achieve the following?
Render the child collection with unique Ids per instance in the list
Post the child collection back with the model
I don't care if I have to enumerate it myself, nor whether it's inside or outside the EditorTemplate; I've just seen it said that this is unnecessary and that MVC works better if you let it handle things. Thanks.
Per #David-Tansey (from this answer - see for a much more detailed explanation):
(changed to suit this question):
When you use #Html.EditorFor(c => c.Employees) MVC convention chooses the default template for IEnumerable. This template is part of the MVC framework, and what it does is generate Html.EditorFor() for each item in the enumeration. That template then generates the appropriate editor template for each item in the list individually - in your case they're all instances of Employee, so, the Employee template is used for each item.
To sum up his explanation on when you use the named template, you are, in effect, passing the entire IEnumerable<Employee> to a template expecting a single Employee by using #Html.EditorFor(company => company.Employees, "EmployeeEditorTemplate").
note: I really only answered this because it had no answer and I stumbled here first. At least now someone doesn't close it and potentially miss an answer without having to go through the rigmarole of getting duplicate question votes, blah blah.
You can read about it here: Hanselman's article
In short, you should write in your view (index.cshtml):
int i=0;
#foreach (var item in Model.Employees)
{
#Html.TextBox(string.Format("Model[{0}].FullName", i), item.FullName)
i++;
}
foreach can be used with #HTML.EditorFor, for an example see below syntax:
#foreach(var emp in #Model)
{
#Html.EditorFor(d => emp.EmployeeName)
#Html.EditorFor(d => emp.Designation)
<br />
}

MVC #Html.Display()

I have something like:
<input type="text" name="TerrMng" id="TerrMng"/>
in HTML. What is the equivalent of the above using #Html.Display?
I tried using: #Html.Display("TerrMng", TerrMng)
but was not successful. Note that I like to use #Html.Display but not sure how to translate the ID value so that it shows up.
The Display method is not for creating input boxes. You'd want to use:
#Html.TextBoxFor(m => m.TerrMng);
or the templated helper method:
#Html.EditorFor(m => m.TerrMng);
I'm assuming that you want to use modelbinding. If not, if you really just want to use a helper to simply make an input tag, use:
#Html.TextBox("TerrMng");
This would be sent to the client:
<input id="TerrMng" type="text" value="" name="TerrMng">
The first 2 methods above would result in the exact same html, if model.TerrMng was "" or String.Empty. If for some reason you don't want the value attribute, you'll need to type it out yourself.
This should do the trick if you are just wanting to display the data and not allow the user to edit the information.
#Html.DisplayFor(m => m.TerrMng);
Edit:
what-is-the-html-displayfor-syntax-for is another question on stackoverflow that may give you some more guidance.
Edit:
TerrMng does not exist on PageLoad so you cannot use the Html.Display in that way. You need to create it and fill its value with the value received from the jQuery. In this case where you would have to do the following:
HTML
#Html.Display("TerrMng"); // This creates the label with an id of TerrMng
jQuery
$("#TerrMng").val(TerrMng); // This puts the value of the javascript variable into the label
You could try something based on this. This is not exact but you could get some idea.
#Html.TextBoxFor(yourmodel => model.yourModelFieldname, null)
#Html.Display() is used instead of #Html.DisplayFor() when your model is not known at compile time, or if you prefer to work with strings, rather than with strong types. For example, these 2 are equivalents (given that your model is some class):
#Html.DisplayFor(m => m.MyProperty)
and
#Html.Display("MyProperty")
But the additional cool feature of the Display() method is that it can also do the lookup in the ViewData, and not just in your Model class. For example, here is a way to display the HTML for the property on a random object, given that we know it has a property named "Blah" (the type of the object doesn't really matter):
#{ ViewData["itsawonderfullife"] = SomeObject; }
<div>#Html.Display("itsawonderfullife.Blah")</div>
This way, we are telling HtmlHelper to look into the ViewData, instead of our Model, and to display the property Blah of a given SomeObject.

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