Passing data from SQL to MVC Array System.String[] Error - c#

I'm passing a Stored Procedure into my Controller, which I then want to send to my View and log each entry of the Stored Procedure into a dropdown menu. My problem is that when I pass the data over to the dropdown menu, all I get is System.String[] instead of the actual value.
Controller:
public ActionResult Dropdown()
{
ViewData["ids"] = db.uspGetAllEventIds().ToArray();
return View();
}
View:
#model IEnumerable<Heatmap.Models.Event>
...
<body>
<div>
<select name="EventId">
#for (var i = 0; i < 6; i++)
{
<option>#ViewData["ids"]</option>
}
</select>
</div>
</body>
Right now I only have a simple for loop set to 6, but I want to iterate through my entire ids array (which should be filled with the values from db.uspGetAllEventIds() and enter them into my dropdown menu. How should I set my loop up to do this?

To use a string array inside ViewData object, you need to cast ViewData into string[] with as operator (or simply a direct casting if you're sure not throwing exception) and later iterate the contents as shown in code below:
#model IEnumerable<Heatmap.Models.Event>
...
<body>
<div>
<select name="EventId">
#{
// a simple (string[])ViewData["ids"] also work here
var ids = ViewData["ids"] as string[];
for (var i = 0; i < 6; i++)
{
<option>#ids[i]</option>
}
}
</select>
</div>
</body>
Working example: .NET Fiddle Demo
NB: You can't access array elements using ViewData["ids"][i] inside for loop because ViewData is a dynamic dictionary which has object type, not object[].
Tip: If you want to iterate through entire ids array, use ids.Length as upper limit value in for loop after casting ViewData.
Reference:
ASP.NET MVC - How to pass an Array to the view?
How to pass an array from Controller to View?

Related

GridMVC Grid Throws Cast exception?

I'm trying to create a simple Grid using GridMVC
Here is my Controller code.
public ActionResult Index()
{
var approvals = new List<Host_Apps>();
approvals = db.Host_Apps.ToList();
return View(approvals);
}
HTML:
#{
Layout = null;
}
#model List<TrackMiPD.Models.Host_Apps>
#using GridMvc.Html
<h2>Index</h2>
<body>
<div>
#Html.Grid(Model,"Index").Columns(columns =>
{
columns.Add(data => data.ApprovalId).Titled("First Name").SetWidth(110);
columns.Add(data => data.Name).Titled("Last Name").SetWidth(110);
columns.Add(data => data.State).Titled("Age").Sortable(true);
columns.Add(data => data.City).Titled("Birth Date").Sortable(true);
}).WithPaging(20)
</div>
</body>
I tried passing IEnumerable instead of List. I vaguely remember long agao I was working with GridMVC that after setting layout= Null it started working So ive put Layout as Null
Here is the error I get
The model item passed into the dictionary is of type
'GridMvc.Html.HtmlGrid1[TrackMiPD.Models.Host_Apps]', but this
dictionary requires a model item of type
'System.Collections.Generic.List1[TrackMiPD.Models.Host_Apps]'.
Try creating a wrapper class for List of Host_Apps, That would have a list of Host_Apps. Then pass it as model to your view.
During access in view access it as:
#Html.Grid(Model.Host_Apps)
The same problem I had but was able to walk away with this.

ASP.NET MVC Binding Nested List

I am using the solution suggested in Mel's space (https://mleeb.wordpress.com/2013/11/23/editing-nested-lists-in-asp-mvc-4/)
for editing nested list
Basically I have my model as below
ProductEditModel
--> ProductAudioEditModel
--> ProductAssetResourceEditModel
I got this working for the below
#Html.EditorFor(c => resource.TrackTitle, null, Html.GetHtmlName("TrackTitle"))
which gives me the correct value when it's edited.
However, I couldnt get this working for DropDownList or I am not able to pick the edited value in the dropdownlist . It always give me the original value in my controller.
#using (Html.BeginCollectionItem("ProductAssetAudios", out parentIdentifier))
{
.....
#foreach (var resource in Model.ProductAssetResources.OrderBy(a => a.ResourceNumber))
{
string childIdentifier = null;
#using (Html.BeginChildCollectionItem("ProductAssetResources", parentIdentifier, out childIdentifier))
{
#Html.HiddenFor(model => resource.AssetResourceStatusId, new { Name = Html.GetHtmlName(childIdentifier, "AssetResourceStatusId") })
#Html.DropDownListFor(model => resource.AssetResourceStatusId, new SelectList(visibleResourceStatuses, "AssetResourceStatusId", "Name", resource.AssetResourceStatusId), new { #class = "inherit-title" }) #Html.ValidationMessageFor(model => resource.AssetResourceStatusId)
}
}
}
The AssetResourceStatusId always holding the original value even though the drop down list is selected for a different value.
I was hoping that the EditorFor and DropDownListFor should be work in the same manner when editing nested list.
Edited
Generated HTML
DropDownListFor
<select class="inherit-title valid" id="ProductAssetAudios_0df86a5c-0a32-4b0f-97ee-3b3254f743d9__ProductAssetResources_c58ba43c-6081-41d4-88fd-d59799c7374e__resource_AssetResourceStatusId" name="ProductAssetAudios[0df86a5c-0a32-4b0f-97ee-3b3254f743d9].ProductAssetResources[c58ba43c-6081-41d4-88fd-d59799c7374e].resource.AssetResourceStatusId" aria-invalid="false"><option value="3">Extra</option>
<option selected="selected" value="2">Found</option>
<option value="8">Ignore</option>
</select>
HiddenFor
<input name="ProductAssetAudios[b5670a6a-7a1d-4c76-86bc-85a05cd144c1].ProductAssetResources[aa378d38-0fb7-4304-9f24-79d0efcb36b9].AssetResourceStatusId" data-val="true" data-val-number="The field AssetResourceStatusId must be a number." data-val-required="The AssetResourceStatusId field is required." id="ProductAssetAudios_b5670a6a-7a1d-4c76-86bc-85a05cd144c1__ProductAssetResources_aa378d38-0fb7-4304-9f24-79d0efcb36b9__resource_AssetResourceStatusId" type="hidden" value="2">
-Alan-
You model contains a collection property named ProductAssetAudios (typeof ProductAudioEditModel) and each object in that collection contains a collection property named ProductAssetResources (typeof ProductAssetResourceEditModel) and each of those objects contains a property named AssetResourceStatusId.
In C# code, if you were to get the AssetResourceStatusId value of the 1st ProductAssetResourceEditModel in the 1st ProductAudioEditModel, your code would be
var id = model.ProductAssetAudios[0].ProductAssetResources[0].AssetResourceStatusId;
Drop the model prefix and that is exactly how the name attribute of the control must be. What the BeginCollectionItem() and BeginChildCollectionItem() methods do is to modify the collection indexers to a Guid and adds a hidden input for the indexer to allow you to dynamically add and remove items from the collection. By default, the DefaultModelBinder will bind collections with zero-based consecutive indexers, unless a value for the indexers is also posted (i.e the reason why the hidden input is added).
In your case, the name attribute for the hidden input is correct, i.e. using
#Html.HiddenFor(model => resource.AssetResourceStatusId, new { Name = Html.GetHtmlName(childIdentifier, "AssetResourceStatusId") })
because your overriding the default name attribute generated by HiddenFor(). You just need to do the same for the DropDownListFor() method, i.e. set the name attribute using new { Name = Html.GetHtmlName(childIdentifier, "AssetResourceStatusId") }. But then you also need to then delete the hidden input because the DefaultModelBinder will only bind the first value that is posted for a property. Note also that you will need to change the ValidationMessageFor() also.
Side note. From the comments it appears that you are not wanting to add and remove items in the view, in which case, do not use the BeginCollectionItem() and BeginChildCollectionItem() methods. Instead, just use nested for loops or custom EditorTemplates for typeof ProductAudioEditModel and ProductAssetResourceEditModel. An example of using for loops would be
for(int i = 0; i < Model.ProductAssetAudios.Count; i++)
{
#Html.TextBoxFor(m => m.ProductAssetAudios[i].SomeProperty)
....
for (int j = 0; j < Model.ProductAssetAudios[i].ProductAssetResources.Count; j++)
{
#Html.DropDownListFor(m => m.ProductAssetAudios[i].ProductAssetResources[j].AssetResourceStatusId, new SelectList(.....)
Refer also this answer for an example of using a nested EditorTemplate.

Binding DropDownListFor In foreach , ASP.NET MVC

Here is my code :
ViewModel
public class FooViewModel{
public Guid BarId { set;get }
}
View :
#model IEnumerable<FooViewModel>
#foreach (var c in Model)
{
<div>
#Html.DropDownListFor(o => c.BarId , (List<SelectListItem>)ViewBag.BarCollection)
</div>
}
the problem is DropDownListFor create the options completely but binding doesn't work.
You cannot use a foreach loop to generate controls for items in a collection. If you inspect the html you will see that you have duplicate name attributes without indexers (and also duplicate id attributes which is invalid html). You need a for loop of a custom EditorTemplate for FooViewModel. Using a for loop (your model must implement IList<T>)
#model IList<FooViewModel>
for (int i = 0; i < Model.Count; i++)
{
#Html.DropDownListFor(m => m[i].BarId, ....)
}
Note the html will now be
<select name="[0].BarId" ..>
<select name="[1].BarId" ..>
etc.

Show list-items in view

I'm pretty new to c# and MVC
I created a FormController.cs with a method like:
public ActionResult Showlist()
{
List<int> list = new List<int>();
list.Add(54);
list.Add(524);
list.Add(23);
list.Add(43);
return View(list);
}
Then I create a view from that method. In my div-tags I put the text like:
<body>
<div>
#Model.ToString();
</div>
</body>
The result is a view with the text System.Collections.Generic.List`1[System.Int32]
My question: What's a good (or best) way to show the values that I added in the list?
Also you need to declare the Model as a list in the cshtml:
#model IEnumerable<int>
#{
foreach (var anInt in Model)
{
<text>#anInt</text>
}
}

IEnumerable property is null after submit

I am new to MVC and have some difficulties understanding this.
To make it simple, I have a "Person" object and this object has an IEnumerable property called "EmailaddressList".
I have generated an edit page through Visual Studio 2012. The main objects properties, are generated on the edit page with textboxes like Name and LastName.
However the list of e-mail addresses in the IEnumerable list of sub-objects are not generated automatically in my view.
This is OK, I have written that code by hand using a tab for each type of e-mailaddress.
So far so good.
Problem:
When I recieve the model (person object) in my HTTP-Post method, the EmailAddressList is null.
Why is it like this, It was not null when I sent it to the view.
I the tab where the e-mailadresses are listed is in a partial view.
Can anyone give me some tips, is it something I'm missing here?*
View-Code
<div id="tabs">
<ul>
#foreach (var item in Model.EmailAddressList)
{
<li>#Html.Label(item.AddressType)</li>
}
</ul>
#foreach (var item in Model.EmailAddressList)
{
<div id="#item.AddressType">
<p>
#Html.TextBoxFor(s => item.EmailAddress, new { #class = "input-xxlarge" })
</p>
</div>
}
</div>
Controller (recieving method)
Here person.EmailAddressList is null
[HttpPost]
public ActionResult Create(Person person)
{
if (ModelState.IsValid)
{
personRepository.InsertOrUpdate(person);
personRepository.Save();
return RedirectToAction("Index");
}
else
{
return View();
}
}
That's because in order to correctly index your fields (so model binder can do it's work), you have to use a for loop.
First, change your IEnumerable to be a List (so we can use an indexor in the view).
Then change your foreach to be the following for loop:
#for (int i = 0; i < Model.EmailAddressList.Count; i++)
{
<div id="#Model.EmailAddressList[i].AddressType">
<p>
#Html.TextBoxFor(m => m.EmailAddressList[i].EmailAddress, new { #class = "input-xxlarge" })
</p>
</div>
}
Based on your update, the reason this doesn't work is because the default model binder only relies on order for a collection of simple data. When it comes to complex type you need to provide the relevant index per item otherwise it doesn't know which item property your referring to e.g.
#for (int i = 0; i < Model.EmailAddressList.Count; i++) {
Html.TextBoxFor(m => m.EmailAddressList[i].EmailAddress) %>
}
See Phil Haack's article on model binding to a list.
It's due to your elements not being ID'd the correct thing for MVC to pick them up on the post back, what you need is:
#Html.EditorFor(model => model.EmailAddressList);
Then, please refer to my post located here on how to make this look to how you want it to.

Categories