Referencing Complex Enumerable in Razor [duplicate] - c#

This question already has answers here:
ASP.NET MVC: No parameterless constructor defined for this object
(28 answers)
Closed 4 years ago.
Why isn't MVC able to bind my List of complex objects? I have read several examples on this topic and nothing has resolved the issue. Consider the following:
Complex Version
I have a ViewModel with a list of complex objects as a property:
public List<XMLVariable> XMLVariables { get; set; }
My View then binds this list of objects to controls:
#for (int i = 0; i < Model.XMLVariables.Count(); i++)
{
#Html.HiddenFor(m => Model.XMLVariables[i].Name)
#Html.TextBoxFor(m => Model.XMLVariables[i].Value)
}
The view displays the variables just fine, but when I submit the form nothing happens. The controller action is not called.
Flattened Version
If I flatten my list of complex objects (XMLVariables) in the same ViewModel:
public List<string> Names { get; set; }
public List<string> Values { get; set; }
And map them in my View:
#for (int i = 0; i < Model.XMLVariables.Count(); i++)
{
#Html.HiddenFor(m => Model.Names[i])
#Html.TextBoxFor(m => Model.Values[i])
}
Upon submission both lists are passed back with the correct values.
Controller Action:
[HttpPost]
public ActionResult Submit(ReportViewModel report, string cmd)
{
}
Form Signature:
#using (Ajax.BeginForm("Submit", "Report", new AjaxOptions {LoadingElementId = "progress", OnSuccess="OnSuccess", OnBegin="OnBegin", OnComplete="OnComplete" }))

The problem was with my "XMLVariable" object. In case anyone has an issue binding custom objects like I did, it appears that MVC constructs a new ViewModel out of the data in the View, rather than passing back a modified version.
I wasn't getting an error for some reason, but the default model binder in MVC was creating XML objects from scratch and trying to call a parameterized constructor I had set up:
public class XMLVariable
{
public string Name {get; set;}
public string Value { get; set; }
//MVC needed this parameterless constructor to bind View to new ViewModel
public XMLVariable()
{ }
//This was getting called before adding the parameterless constructor and failing
public XMLVariable(string name, string value)
{
Name = name;
Value = value;
}
}
Thank you to ChetanRanpariya for pointing me to the network tracing tool that caught the issue.
If someone has the reputation for it, please mark this as a duplicate of this question which I found after the fact.

Related

DropDownList Error, System.InvalidOperationException

Create.cshtml
#Html.DropDownList("KategoriId",null,htmlAttributes: new { #class="form-control"})
MakaleController.cs
public ActionResult Create()
{
ViewBag.KategoriId = new SelectList(db.Kategoris, "KategoriId", "KategoriAdi");
return View();
}
but it's say When I make a post request,
The ViewData element with the 'KategoriId' key is of type 'System.Int32', but of type 'IEnumerable '.
How to fix this error, I tried many ways but it did not happen. He says that the value is generally null.
Kategori.cs (!! NOT KATEGORIS, i create with code first)
[Table("Kategori")]
public partial class Kategori
{
public int KategoriId { get; set; }
[StringLength(50)]
public string KategoriAdi { get; set; }
public virtual Makale Makale { get; set; }
}
Updated
Your dropdown definition is not right
it should look like below
#Html.DropDownListFor(m => m.CategoryID, Model.CategoryList, "-Please select-")
you pass null to Dropdownlist, but it is supposed to be at least an empty list
You also try to keep dropdownlist data in viewbag. it is supposed to be a List property in the view model.
P.S: using native words in variable names are not acceptable in global projects due to readability issues =)
P.S: You can check a similar question and answer from here The ViewData item that has the key 'XXX' is of type 'System.Int32' but must be of type 'IEnumerable<SelectListItem>'

Sending list of data received from api in controller to View [duplicate]

This question already has answers here:
The model item passed into the dictionary is of type .. but this dictionary requires a model item of type
(7 answers)
Closed 5 years ago.
I am trying to send list received in var result to my view in order to display it.
I am able to get values in Controller for var result but when I want to pass those values in order to use in View I am getting error on result.ResponseObject saying System.Collection cant be converted to Base. Basically var result has a list.
But i am getting error.
The model item passed into the ViewDataDictionary is of type 'System.Collections.Generic.List`1[Project.Models.Document]', but this ViewDataDictionary instance requires a model item of type 'Project.Models.Document'.
Controller:
public async Task<IActionResult> GetAllSavedDocument(int id)
{
var result = await _applicationService.GetAllSavedDocument(id);
return HandleResult(() => View("_Alldocuments", result.ResponseObject), result.Status);
}
My model contains information
public class Document
{
public int ApplicationID { get; set; }
public string URI { get; set; }
public string Author { get; set; }
}
Your view needs to reference a list not a single object.
change #model Document to #model List<Document> or #model IEnumerable<Document> int the "_Alldocuments" view

Loop over ModelView in razor

I have created razor forms that do a #foreach to loop over the model and produce my rows. Now I am trying to to the same thing over a ViewModel, and I get the following error.
Unable to create a constant value of type 'MyProject.Models.SupportContact'.
Only primitive types or enumeration types are supported in this context.
My #model is being passed in as IQueryable(MyProject.Models.SupportContactFormView), and SupportContact is part of that ViewModel, but I can't seem to reference it in the model.
I can do this:
#foreach (var item in Model ) {
#Html.DisplayFor(modelItem => item.SupportContact.Contact)
But then I will get the error above when it actually try to process my cshtml. I have tried to do:
#foreach (MyProject.Models.SupportContact item in Model ) {}
But I can't drill down thru the model to get the SupportContacts. How can I get to a my SupportContact class inside my ViewModel?
Thanks
Edit:
Here is the ViewModel declaration:
public class SupportContactFormView
{
public SupportContact SupportContact { get; private set; }
public SelectList Priorities { get; private set; }
public SelectList ContactTypes { get; private set; }
public String Group_COde { get; private set; }
}
In your case, I would create a viewmodel specifically for that view and set it up like so
ViewModel Class
public class IndexViewModel
{
public IList<SupportContact > SupportContacts { get; set; }
}
Controller
public ActionResult Index()
{
var viewModel = new IndexViewModel();
viewModel.SupportContacts = IQueryableListOfContacts.ToList();
return View(viewModel)
}
View
#model IndexViewModel
#foreach (var contact in Model.SupportContacts) {
#Html.DisplayFor(_ => contact.Contact)
You need to play around a bit with the code, as I doubt I have the property names/action method name correct first time, but I hope you get the basic idea. Create a viewmodel class that the view file makes use of, and dump the contact details into a List. That will make it easier for you.
EDIT:
Actually, I think I got the wrong end of the stick here. The error "Unable to create a constant value of type 'MyProject.Models.SupportContact'.
Only primitive types or enumeration types are supported in this context." means that there is something wrong with your LINQ data.
Are you using Entity Framework or Linq2SQL? Ignore my answer about the viewmodel appraoch, your actual issue is LINQ related. Try iterating over the list of contacts in an ActionMethod instead e.g.
public ActionMethod Index()
{
foreach (var contact in whateveryourcontactslistis) {
}
// ....
}
If you still have a problem here, then it's your LINQ data at fault.

using ViewBag to pass a model object

I'm trying to sort my result page (which is in another view than the filtration page). I have faced this weird issue I do not understand why keeps happening to me.
All the codes provided in very short form, please ask me if you need any other parts of my code for more information.
My Index view(where user filters results):
#model IEnumerable<Cars.Models.CarSearch>
#using (Html.BeginForm("SearchResult", "Home", FormMethod.Post,
new
{
id = "CategoryFormID",
data_modelListAction = #Url.Action("ModelList"),
data_makeListAction = #Url.Action("MakeList"),
data_editionListAction = #Url.Action("EditionList")
}))
{
<label>Make</label>
<select id="MakeID" name="carMake">
<option>All Makes</option>
</select>
}
My SearchResult view:
#model IEnumerable<Cars.Models.Car>
Make
My model:
public class Car
{
public String Make { get; set; } //is my table model
}
public class CarFilter {
public String carMake { get; set; }
}
public class CarSearch {
public CarFilter CarFilter { get; set; }
public byte PageSize { get; set; }
public short PageNumber { get; set; }
public int TotalRows { get; set; }
}
My Controller:
public ActionResult SearchResult(String sortOrder, CarFilter filters)
{
ViewBag.CurrentFilters = filters;
return View();
}
All I'm trying to do is to get carMake from Index post it to controller in CarFilter form (since in my code there are LOTS of fields in the form and I don't want to write them all down) and when user clicks on sort by Make it GET the SearchResult method and it's supposed to set filters = ViewBag.CurrentFilters which is the value user inputted from beginning.
Now the funny part is, when I replace CarFilter filters with String carMake and other places respectively. It works like a charm.
My question:
Why?
How can I do this with CarFilter filters?
UPDATE:
Problem is that filters = ViewBag.CurrentFilters in my SearchResult view does not work with the type CarFilter, because it keeps giving me NULL value when user clicked on the sort by Make.
Second UPDATE:
I tried changing filters = ViewBag.CurrentFilters with CarFilter = ViewBag.CurrentFilters. Now CarFilter filters in my SearchResult(...)method in my controller is not and null object, but ALL the values of the objects in the model class is null (which shouldn't be). I mean the filters object exists but it seems like the values of CarFilter class in my model haven't been passed by ViewBag.CurrentFilters to the view.
when you canged the name it worked because framework found property name and the bind it to what you have within action parameters doesnt work so nicely with objects. My advice is to stick with simple types
Here is similiar case:
How to send model object in Html.RenderAction (MVC3)
Its not a ViewBag problem thants how it works in general. Its the prime reason for using flatted models :/

MVC3 format nullable DateTime in TextBoxFor without changing model

I am new to MVC3 and am still trying to pick up on the good programming practices. I had a heck of a time trying to format how a DateTime? was being displayed in my MVC3 project that doesn't have an explicit ModelName.cs file associated with the class the date was coming from.
We had a database already in place and use a .edmx (ours is called Pooling.edmx) that we get our models from. I obviously didn't want to edit the designer file to fit this widely accepted solution: Date only from TextBoxFor().
I then tried another solution which I found here: Using Html.TextBoxFor with class and custom property (MVC)
which uses:
#Html.TextBoxFor(m => m.Name, new { data_bind="value: Name", #class = "title width-7" })
This worked as I was able to use custom attributes, add class names, and set a Value all at once.
I transformed this:
#Html.TextBoxFor(m => Model.PrePoolOwner.OglDateEffective, new Dictionary<string, object> { { "class", "check-dirty input-small datePicker" }, { "data-original-value", #Model.PrePoolOwner.OglDateEffective } })
into this (which seems really ugly...and leads to me to the question):
#Html.TextBoxFor(m => Model.PrePoolOwner.OglDateEffective, new { data_original_value = Model.PrePoolOwner.OglDateEffective.HasValue ? Model.PrePoolOwner.OglDateEffective.Value.ToString("MM/dd/yyyy") : null, #class = "datePicker check-dirty", #Value = Model.PrePoolOwner.OglDateEffective.HasValue ? Model.PrePoolOwner.OglDateEffective.Value.ToString("MM/dd/yyyy") : null })
Is it better to find and use these other ways (like underscores will translate into dashes, etc) to display the information, or should I be having a ModelName.cs file to change how it is displayed at the Model level?
For some reason I feel having a huge Pooling.edmx file, that maps out our database, is limiting us now and will in the future on how we access/present/change data as the website evolves.
To get a "PrePoolOwner" object which is called up above by Model.PrePoolOwner.OglDateEffective, we have a PrePoolOwnerRow.cs file that does:
namespace OCC_Tracker.Models
{
public class PrePoolOwnerRow
{
public bool Dirty { get; set; }
public bool Delete { get; set; }
public PrePoolOwner PrePoolOwner { get; set; }
public PrePoolOwnerRow(PrePoolOwner owner)
{
this.Dirty = false;
this.Delete = false;
this.PrePoolOwner = owner;
}
public PrePoolOwnerRow()
{ }
}
}
We then call at the top of our .cshtml file
#model OCC_Tracker.Models.PrePoolOwnerRow
Ok, so a few suggestions.
First, in your example, PrePoolOwnerRow is your view model. This, in itself, is fine. But the code smell is where you expose PrePoolOwner -- a domain entity -- through your view model, PrePoolOwnerRow.
So first thing I would suggest is to update your view model to something more like this:
public class PrePoolOwnerModel
{
public bool Dirty { get; set; }
public bool Delete { get; set; }
public DateTime? OglDateEffective { get; set; }
public String OglDateEffective { get; set; }
// Other public properties here that map to properties on your PrePoolOwner entity.
}
All I did here was drop the reference to the domain model, and replace it with (a placehold comment to) the properties from your model, needed by your view.
In your controller, fetch your PrePoolOwner model, and map it to your view model using AutoMapper (this is a hypothetical example, as I don't know what your view is doing):
public ViewResult Index(int id)
{
PrePoolOwner entity = myservice.GetPrePoolOwner(id);
PrePoolOwnerModel model = Mapper.Map<PrePoolOwnerModel>(entity);
return View(model);
}
Now, to address the issue w/ the DateTime textbox, you should look at using MVC Editor Templates (this is another subject altogether, but Google it to find many topics covering the subject). This gives you more flexibility and re-usability over rendering elements of like types (i.e. DateTime).
But, aside from using that, you can add another property to your model, and use AutoMapper to set the DateTime appropriately. So, something like this in your controller, execpt you would set up a mapping in AutoMapper to handle this:
public class PrePoolOwnerModel
{
....
public String OglDateEffectiveValue { get; set; }
....
}
public ViewResult Index(int id)
{
....
model.OglDateEffectiveValue = model.OglDateEffective.HasValue ?
model.OglDateEffective.Value.ToString("MM/dd/yyyy") :
String.Empty;
....
}
Once that is set up, you can just use this new model property (OglDateEffectiveValue) for your attributes on your textbox.
I know there's a lot I covered there, but dig in and experiment with modeling your view models like this, and using AutoMapper to map the data to your view model exactly like you need it to be on your view.
Keep your view logic very simple. Avoid using anything crazy beyond the occasion loop, and maybe an if conditional here or there.

Categories

Resources