MVC Model Binding not binding simple object on nested loops - asp.net-mvc

Having this structure:
Index.cshtml:
#foreach (var category in Model) {
#Html.DisplayFor(m => category.Products)
}
Products.cshtml:
...
#Html.Partial("_AddToCartProductViewModel", new CheckoutVC.Models.ModelView.AddToCartProductViewModel(Model))
...
_AddToCartProductViewModel.cshtml :
#model CheckoutVC.Models.ModelView.AddToCartProductViewModel
#using (Ajax.BeginForm("AddToCart", "Cart", new AjaxOptions { LoadingElementId = "loading" + Model.IdProduct, OnSuccess = "showMessage", UpdateTargetId = "cart_widget" })) {
#Html.HiddenFor(m => m.IdProduct)
#Html.HiddenFor(m => m.IdPrescriptionType)
#Html.HiddenFor(m => m.PrescriptionRequired)
#Html.HiddenFor(m => m.Quantity)
<span class="p-r-s">#Html.LabelFor(m=>m.IdPrescriptionType)</span>
#Html.DropDownListFor(m=>m.IdPrescriptionType, new SelectList(Model.PrescriptionOptions, "Item1", "Item2"), String.Empty)
<button class="action add #Html.PrescriptionRequiredCss(Model.PrescriptionRequired)" type="submit">agregar al carrito<img class="loading" id="loading#(Model.IdProduct)" alt="" src="#Url.Content("~/Content/images/shared/loading_16x16.gif")" width="16" height="16" /></button>
}
With this AddToCartProductViewModel.cs constructor :
public AddToCartProductViewModel(ProductCheckoutMinVO product, int quantity = 1) {
IdProduct = product.Id;
PrescriptionRequired = product.PrescriptionRequired;
Quantity = 1;
Promotions = product.Promotions;
}
[Required]
[Min(1)]
public int IdProduct { get; set; }
public int Quantity { get; set; }
public bool? PrescriptionRequired { get; set; }
[Min(0)]
public int IdPrescriptionType { get; set; }
MVC generate this Request on submit:
category.Products[0].IdProduct:826
category.Products[0].IdPrescriptionType:0
category.Products[0].PrescriptionRequired:False
category.Products[0].Quantity:1
category.Products[0].IdPrescriptionType:1
X-Requested-With:XMLHttpRequest
Problem is, my controller CartController.cs :
public RedirectToRouteResult AddToCart(AddToCartProductViewModel product, FormCollection form, string returnUrl = null) {
...
}
the FormCollection (form) does receive the parameters while the AddToCartProductViewModel (product) does NOT bind.
I have some ideas why properties are not binding to product and how im doing some magic here and there to get a single-object form populated from some nested loops (in which one would expect a collection object in request [one being the framework]), yet cannot find a elegant solution to have this kind of form-scenario bind to AddToCartProductViewModel.
I can make it work 'somehow' using the properties directly into the AddToCart method but then i lose validation (dataannotations) on the modelview.
How can I make MVC bind these properties to the AddToCartProductViewModel view model

I think you could try:
public RedirectToRouteResult AddToCart([Bind(Prefix="category.Products[0]")]AddToCartProductViewModel product, FormCollectionform, string returnUrl = null) {
...
}

Solving some problem brings up another.
Ended up doing it manually with html tags and leave the data annotations validation out.
may be a messed up viewmodel problem i just don't know and don't have the time or resources to make it work as i would like right now. Maybe I'll try to address in the future.
Don't know if there's a way to close the question or what to do? editor feel free to update this as you pleases. :D

Related

ASP.NET MVC: Getting values from TextBoxFor, TextBox, DropDownList and TextArea

In ASP.Net MVC I am opening one view from another view. The first view sends two values to the second view. In the second view the user can send an email.
I am having two problems.
The first problem is that the two values that I send from the first view aren't being shown in my second view.
The second problem is that I can't get the email form to trigger my email function in the controller.
Here's a more detailed explanation.
My first view named ViewOne is using the controller ControllerOne. In ViewOne I have the following code to call the second view, ViewTwo:
#Html.ActionLink("Go to second view", "ViewTwo", "Home", new { firstval = firstval, secondval = secondval }, null)
When the ActionLink is clicked, the following function in the controller HomeController is called:
public ActionResult ViewTwo(string firstval, string secondval)
{
MyModel model = new MyModel();
model.firstval = firstval;
model.secondval = secondval;
var list = new SelectList(new[]
{
new {ID="1",Name="One"},
new{ID="2",Name="Two"},
new{ID="3",Name="Three"},
},
"ID", "Name", 1);
model.myList = list;
return View(model);
}
So in the controller HomeController I attempt to populate the model myModel with the values I get from the first view, ViewOne.
The model MyModel looks like this:
public class MyModel
{
public string firstval { get; set; }
public string secondval { get; set; }
public IEnumerable<SelectListItem> myList { get; set; }
[Required]
[DisplayName("My name")]
public string reporter { get; set; }
[Required]
[DisplayName("Description")]
public string description { get; set; }
[DisplayName("Dropdown")]
public string myDropDownListValue { get; set; }
}
The view ViewTwo looks like this:
#model myapp.Models.MyModel
#{ ViewBag.Title = "Send e-mail"; }
<hgroup class="title">
<h1>#ViewBag.Title</h1>
<h2>#ViewBag.Message</h2>
</hgroup>
#using (Html.BeginForm("sendEmail"))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary()
<fieldset>
<legend>Send e-mail</legend>
<p>First value:</p>
<p>#Html.LabelFor(m => m.firstval)</p>
<p>Second value:</p>
<p>#Html.LabelFor(m => m.secondval)</p>
<p>Reporter</p>
<p>#Html.TextBoxFor(m => m.reporter)</p>
<p>Dropdownlist</p>
<p>#Html.DropDownListFor(m => m.myDropDownListValue, Model.myList as SelectList)</p>
<p>Description:</p>
<p>#Html.TextAreaFor(m => m.description, new { #cols = 150, #rows = 5})</p>
<input type="submit" value="Send e-mail"/>
</fieldset>
}
In the controller HomeController, which is the same controller that has the ViewTwo() function that gets triggered right before the above form gets drawn, I have the following function:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult sendEmail(ContactModel model) // (string keyword, string partofspeech, string reporter, string category, string description, string acceptance)
{
// code to send email
}
So I want this function, sendEmail, to get triggered whenever I submit the form. But that doesn't happen. What happens when I click the submit button (labeled "Send e-mail") is that the view ViewTwo gets reloaded and the ActionResult ViewTwo() in the controller HomeController gets triggered. This is my second (and biggest) problem.
Also, my first problem is that
<p>#Html.LabelFor(m => m.firstval)</p>
Doesn't show the value that gets sent from the first view. It shows the string "firstval". Before the form is drawn I can see in the function ViewTwo() that the value gets correctly sent from the first view.
Any ideas?
EDIT:
Second problem solved. See my reply below.
You have a few options, normally with a postback you would submit the form with an <input type="submit" value="sendEmail" />, the values in the form would be represented in a ViewModel like:
public class EmailFormViewModel()
{
public string value1 {get; set;}
public string reporter {get; set;}
//More properties on the form
}
Your endpoint would look like this:
[HttpPost]
public ActionResult SendEmail(EmailFormViewModel model)
{
//Send the email
}
If you still want to use a hyperlink to submit the form, which natively performs a GET request, you will can catch the click with javascript, and manually send the form via Ajax.
Something like:
$('#sendEmail').click(function(e) {
e.preventDefault();
$.ajax({
type: 'POST',
data: $('#formId').serialize(),
url: '/controllerName/sendemail'
}).done(function(response) {
//Do something on success response
});
});
Update:
You should also decorate your post action sendEmail with [ValidateAntiForgeryToken] and add a #Html.AntiForgeryToken() within the form. This will help protect against cross site forgery requests.
You can build your form, endpoint and model like this:
#using (Html.BeginForm("sendEmail"))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary()
<p>#Html.LabelFor(m => m.value1)</p>
<p>#Html.EditorFor(m => m.value1)</p>
<p>#Html.LabelFor(m => m.reporter)</p>
<p>#Html.EditorFor(m => m.reporter)</p>
<p>#Html.LabelFor(m => m.myDropDownListValue)</p>
<p>#Html.DropDownListFor(m => m.myDropDownListValue, Model.myList as SelectList)</p>
<p>#Html.LabelFor(m => m.myTextAreaValue)</p>
<p>#Html.TextAreaFor(m => m.myTextAreaValue, new { #cols = 150, #rows = 5})</p>
<input type="submit" value="Send Email"/>
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult SendEmail(myModel model)
{
//Send the email
}
public class myModel
{
public IEnumerable<SelectListItem> myList { get; set; }
[DisplayName('Value 1')]
public string value1 { get; set; }
[DisplayName('Reporter')]
public string reporter { get; set; }
[DisplayName('Text Area')]
public string myTextAreaValue { get; set; }
[DisplayName('Dropdown')]
public string myDropDownListValue { get; set; }
}
As long as you are already on the same controller, it will postback to /controllername/sendemail with the form data inside the post. You should also look up attributes on your models, you can enforce descriptions and validations for example. Check here for more details, its MVC 2 but still relevant.
If you really want to be able to GET the values instead of POSTing them, change the form's action to GET and change the target to be sendEmail
Remove the ActionLink and replace it with a simple submit button
I know you said you wanted to keep the ActionLink, but this will achieve the same thing
I managed to solve my first problem. Once I specified which controller the function sendEmail is in, that code finally got triggered. Like so:
#using (Html.BeginForm("sendEmail", "Home"))
Now if I can only figure out why
<p>#Html.LabelFor(m => m.firstval)</p>
isn't working then I'm home safe.
It actually prints out the string "firstval" instead of taking the value of the string variable firstval that I set in the model. (See my first post for more detailed explanation).
EDIT:
I fixed that last problem. The very werid thing is that the above code with LabelFor doesn't work. But if I do this instead:
<p>#Model.firstval</p>
then I get the value. But it doesn't get sent back to the controller when the form is submitted. But that I solved with:
#Html.HiddenFor(m => m.firstval)
So HiddenFor works for me, LabelFor doesn't.
Case closed. I'm throwing the "solved checkmark" to the guy who gave me all that help here above. He did awesome. But the real solution is in this post. :)

MVC Ajax Begin Form save and then using the ID returned after saving in the view

I have a model with two entities (linked with a foreign key) and each entity has its own tab rendered using a partial view. Each tab also has its own Ajax form. When I save the entity in the first tab I now have the ID of the entity which I want to return to the two partial views in order to enable the saving of the second entity or saving updates to the first entity. I cannot get this value back to the view.
The model:
public class Entity1
{
int ID1 { get; set; }
[Some attributes]
string field1 { get; set; }
}
public class Entity2
{
int ID2 { get; set; }
[Some attributes]
string field2 { get; set; }
}
public class MyModel
{
Entity1 entity1 = new Entity1()
Entity2 entity2 = new Entity2()
}
The controller:
public class MyController : Controller
{
[HttpGet]
public ActionResult Index()
{
var model = new MyModel();
model.entity1.ID1 = 0;
model.entity2.ID2 = 0;
return PartialView(model);
}
[HttpPost]
public ActionResult Index(MyModel model)
{
SaveMyModel(model)
// have tried ModelState.Clear(); here
return PartialView(model);
}
}
And finally one of the two partial views
#model MyModel
#using (Ajax.BeginForm("Index", "Home",
new AjaxOptions
{
HttpMethod = "POST"
}
))
{
#Html.LabelFor(m => m.Entity1.field1)
#Html.EditorFor(m => m.Entity1.field1)
#Html.HiddenFor(m => m.Entity1.ID1)
<div class="form-actions">
<button type="submit">
Next section</button>
</div>
}
My save function either inserts or updates depending on the value of ID1.
The problem is that the values of ID1 always stays at zero and the hidden field is not refreshed on the return. I have tried single stepping through the razor refresh and the correct ID is being sent to the view.
The above is a simplification but it does encapsulate the problem.
Thank you in advance.
UPDATE
I can get this to work if:
I only have a single entity in my model
I add ModelState.Clear(); before the save
I was running into the same issue on my project. The only way for me to resolve it was to not include the id when the it was 0. That way when it came back the id was replaced. So in your example you would do the following:
#model MyModel
#using (Ajax.BeginForm("Index", "Home",
new AjaxOptions
{
HttpMethod = "POST"
}
))
{
#Html.LabelFor(m => m.Entity1.field1)
#Html.EditorFor(m => m.Entity1.field1)
#if(Model.Entity1.ID1 !=0){
Html.HiddenFor(m => m.Entity1.ID1)
}
<div class="form-actions">
<button type="submit">
Next section</button>
</div>
}
You need to remove the value from the ModelState if you intend to modify it in your POST controller action:
ModelState.Remove("Entity1.ID1");
This way you don't need to clear the entire ModelState using ModelState.Clear but only the value you are actually modifying. This way the Html helper will pick the value from your model and not the one in the ModelState.

ViewModel Implementation in ASP.NET MVC - Is this code best practice?

I've just started to get into using ViewModels. Can you guys check out this code to see if I'm following best practice? Is there anything out of the ordinary? Would you do the validation differently?
Sorry if code is lengthy (there's so many parts to it). I've tried to make it as easy to understand as possible.
Thanks!
Model
public class CustomerModel
{
[Required(ErrorMessage="Primer nombre!")]
public string FirstName { get; set; }
[Required(ErrorMessage="Segundo nombre!")]
public string LastName { get; set; }
[Required(ErrorMessage="Edad")]
public int? Age { get; set; }
public string State { get; set; }
public string CountryID { get; set; }
[Required(ErrorMessage="Phone Number")]
public string PhoneNumber { get; set; }
}
ViewModel
public class CustomerViewModel
{
public CustomerModel Customer { get; set; }
public string Phone1a { get; set; }
public string Phone1b { get; set; }
public string Phone1c { get; set; }
}
Controller
public ActionResult Index()
{
CustomerViewModel Customer = new CustomerViewModel()
{
Customer = new CustomerModel(),
};
return View(Customer);
}
[HttpPost]
public ActionResult Index(CustomerViewModel c)
{
//ModelState.Add("Customer.PhoneNumber", ModelState["Phone1a"]);
// Let's manually bind the phone number fields to the PhoneNumber properties in
// Customer object.
c.Customer.PhoneNumber = c.Phone1a + c.Phone1b + c.Phone1c;
// Let's check that it's not empty and that it's a valid phone number (logic not listed here)
if (!String.IsNullOrEmpty(c.Customer.PhoneNumber))
{
// Let's remove the fact that there was an error!
ModelState["Customer.PhoneNumber"].Errors.Clear();
} // Else keep the error there.
if (ModelState.IsValid)
{
Response.Write("<H1 style'background-color:white;color:black'>VALIDATED</H1>");
}
return View("Index", c);
}
}
View
#model MVVM1.Models.CustomerViewModel
#using (Html.BeginForm("Index", "Detail"))
{
<table border="1" cellpadding="1" cellspacing="1">
<tr>
<td>#Html.LabelFor(m => m.Customer.FirstName)</td>
<td>
#Html.TextBoxFor(m => m.Customer.FirstName)
#Html.ValidationMessageFor(m => m.Customer.FirstName)
</td>
</tr>
<tr>
<td>#Html.LabelFor(m => m.Customer.LastName)</td>
<td>
#Html.TextBoxFor(m => m.Customer.LastName)
#Html.ValidationMessageFor(m => m.Customer.LastName)
</td>
</tr>
<tr>
<td>#Html.LabelFor(m => m.Customer.Age)</td>
<td>
#Html.TextBoxFor(m => m.Customer.Age)
#Html.ValidationMessageFor(m => m.Customer.Age)
</td>
</tr>
<tr>
<td>#Html.LabelFor(m => m.Customer.PhoneNumber)</td>
<td width="350">
#Html.TextBoxFor(m => m.Phone1a, new { size="4", maxlength="3" })
#Html.TextBoxFor(m => m.Phone1b)
#Html.TextBoxFor(m => m.Phone1c)
<div>
#Html.ValidationMessageFor(m => m.Customer.PhoneNumber)
</div>
</td>
</tr>
<tr>
<td></td>
<td>
<input type="submit" value="Submit" /></td>
</tr>
</table>
}
One thing that jumps out at me is this:
if (ModelState.IsValid)
{
Response.Write("<H1 style'background-color:white;color:black'>VALIDATED</H1>");
}
return View("Index", c);
Remember that view models are good for passing data to your controller AND back to your model. I recommend you add an IsValid property to your view model and then setting that to true instead of calling Response.Write. Then simply add this to the top of your partial view:
#if (Model.IsValid)
{
<H1 style'background-color:white;color:black'>VALIDATED</H1>
}
You can also get to ModelState in your view but some would argue that isn't a best practice. However, if you don't want to add a property to your model for something you can just see in your view you can just do this:
#if (ViewData.ModelState.IsValid)
Another nitpicky thing is that MVC validation attributes are typically used for validation on the UI. This validation can be reused in other areas but in some cases is sub-optimal. Also, you may not always be able to modify your domain models. Therefore, to keep all of my UI validation in one place I usually wrap my domain models in my view models so you get something like this:
public class CustomerViewModel
{
public CustomerModel Customer { get; set; }
[Required(ErrorMessage="Primer nombre!")]
public string FirstName
{
get { return Customer.FirstName; }
set { Customer.FirstName = value; }
}
...
This may seem redundant and isn't always worth the effort but it is a good practice to consider when using Entity Framework domain models or other classes which are difficult or impossible to modify.
I'm just getting the hang of MVC myself, but I researched this same topic yesterday and came to the conclusion that one should not directly include a model object in the ViewModel. So my understanding is that it would be a bad practice to include your CustomerModel directly in the CustomerViewModel.
Instead, you want to list out each of the properties from CustomerModel that you want to include in your ViewModel. Then you either want to manually map the data from CustomerModel to the CustomerViewModel or use a tool like AutoMapper which does it automatically with a line of code like this inside of your action method:
public ViewResult Example()
{
// Populate/retrieve yourCustomer here
Customer yourCustomer = new CustomerModel();
var model = Mapper.Map<CustomerModel, CustomerViewModel>(yourCustomer);
return View(model);
}
In this case, Mapper.Map will return a CustomerViewModel that you can pass to your View.
You will also need to include the following in your Application_Start method:
Mapper.CreateMap<CustomerModel, CustomerViewModel>();
In general I found AutoMapper pretty easy to get to work. It's automatic when the field names match, if they don't or you have a nested Object, you can specify those mappings in the CreateMap line. So if your CustomerModel uses an Address object instead of individual properties, you would do this:
Mapper.CreateMap<CustomerModel, CustomerViewModel>()
.ForMember(dest => dest.StreetAddress, opt => opt.MapFrom(src => src.Address.Street));
Please anyone correct me if I'm wrong as I'm just getting my head around MVC as well.
I would say your ViewModel implementation is pretty standard. You are using the ViewModel to act as the intermediate object between your View, and your Domain Model. Which is good practice.
The only thing I'd be weary about is how you handle Model errors, and also your ViewModel should have some attributes. For instance, you might want to use the RegularExpressionAttribute Class:
public class CustomerViewModel
{
public CustomerModel Customer { get; set; }
[RegularExpression(#"^\d{3}$")]
public string Phone1a { get; set; }
[RegularExpression(#"^\d{3}$")]
public string Phone1b { get; set; }
[RegularExpression(#"^\d{4}$")]
public string Phone1c { get; set; }
}

How to have one RadioButtons group display in EditorTemplates in ASP.Net MVC(3)

I have a list of items from which I want the user to be able to input some value select one
But the radio-buttons generated by the EditorTemplate are named like "Item[x].SelectedItemId" so they are totally independent from each other and I can't get the value...
Let's go show some code.
The model:
public class FormModel
{
public List<ItemModel> Items { get; set; }
public int SelectedItemId { get; set; }
}
public class ItemModel
{
public int ItemId { get; set; }
public string ItemName { get; set; }
public string SomeString { get; set; }
}
The view:
#model FormModel
#using (Html.BeginForm())
{
#Html.EditorFor(m => m.Items)
}
The editor template:
#model ItemModel
#Html.RadioButton("SelectedItemId", Model.ItemId)
#Model.ItemName <br/>
#Html.TextBoxFor(m => m.SomeString) <br/>
UPDATE
This is what I want:
This is what I get:
As a result, FormModel.SelectedItemId never gets the value of any radio-button.
What am I doing wrong?
It appears as though you are aware that setting the names for radio buttons to be the same is necessary to make them work. However, when you do that in an editor template by using the line of code #Html.RadioButton("SelectedItemId", Model.ItemId), MVC 3 will take into consideration that you are in an editor template for Items and prepend items[n].
This would create a name of something like name="Items[0].SelectedIndex". This would be fine if it weren't for the fact that the next radio button would be `name="Items[1].SelectedIndex".
One way to solve this is to not use an editor template and use a foreach loop instead. Here is some code that I was able to get functional. I confirmed that model-binding worked for the SelectedIndex.
#model FormModel
#{
ViewBag.Title = "Index";
}
#using (Html.BeginForm())
{
foreach (var item in Model.Items)
{
#Html.RadioButtonFor(x => x.SelectedItemId, item.ItemId)
#item.ItemName <br/>
#Html.TextBoxFor(m => item.ItemName) <br/>
}
<input type="submit" value = "submit" />
}
I had same problem, and we solved it with this piece of code.
#Html.RadioButton("", Model.Id, Model.Selected, new { Name = "deliveryMethod" })
You need to put Name property explicitly, so it will be used instead name that you get after EditorFor executes.

If Scott Allen can make it work ... why can't I? Should be a simple drop down list - MVC3

I have been wrestling with what should be a very simple thing for weeks now. I simply want to create a dropdownlist in asp.net mvc 3 razor html page and I want the data for the dropdownlist to come from a model.
My Model is as follows which is in the Models.Project namespace.
public class Project
{
public Project()
{
CategoryId = 0;
Name = "";
Description = "";
//Categories = new Dictionary<int, string>();
Entities _db = new Entities(); //ef4
CateogoriesList = from c in _db.Categories
orderby c.Name
select c.Name;
}
public int CategoryId { get; set; }
[Required]
[DataType(DataType.Text)]
[Display(Name = "Project Name")]
public string Name { get; set; }
[Required]
[DataType(DataType.MultilineText)]
[Display(Name = "Project Description")]
public string Description { get; set; }
public IQueryable<string> CateogoriesList;
}
My Controller action is as follows
public ActionResult Create()
{
Models.Project.Project proj = new Models.Project.Project();
return View(proj);
}
My Razor view has the following relevant code ...
#{
ViewBag.Title = "Create";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#model Models.Project.Project
#using (Html.BeginForm())
{
#Html.ValidationSummary(true);
<fieldset>
<legend>Submit Your Request</legend>
<div class="editor-label">#Html.LabelFor( Model => Model.CateogoriesList )</div>
<div class="editor-field">
#Html.DropDownList("Category", new SelectList( Model.CateogoriesList ) )
</div>
</fieldset>
<p><input type="submit" value="Send for RFP" /></p>
}
The problem is that I get the following error ...
Compiler Error Message: CS0135: 'Model' conflicts with the declaration 'System.Web.Mvc.WebViewPage<TModel>.Model'
I saw the following clip make it work with the ViewBag ... and I don't understand why it won't work when I include the list in the model.
http://www.pluralsight-training.net/microsoft/players/PSODPlayer.aspx?author=scott-allen&name=mvc3-building-data-i&mode=live&clip=0&course=aspdotnet-mvc3-intro
I have also seen that there are a lot of people that seem to have trouble with this simple task but in my googling ... I haven't come across anyone with the same error in trying to create a drop down list.
I would appreciate any suggestions that you or anyone may have. The only thing that I've come up with is that the SelectList constructor takes a parameter of type System.Collections.IEnumerable and what I'm trying to pass it is System.Collections.Generic.IEnumerable ... or something close to it ... and I don't know how to cast it appropriately ... though I don't think I should have to ... if it works with a viewbag as the means of transportation why doesn't it work with the model as the means of transportation?
Thanks,
EDIT:======================
The problem was to do with the type of object the selectList constructor would accept. For some reason it wouldn't accept a generic IQueryable but when I cast the result from the entity framework using the cast extension method toArray it suddenly worked.
So my model becomes ...
public class Project
{
public Project()
{
Riebro.RiebroEntities _db = new Riebro.RiebroEntities();
CategoriesList = (from c in _db.Categories
orderby c.Name
select c.Name).ToArray<string>();
}
[Display(Name = "Choose a category")]
public string[] CategoriesList;
}
note the .ToArray on the end of the query and then suddenly
#Html.DropDownList("Category", new SelectList(Model.CategoriesList))
works. Though I am going to point out the Model keyword here seems to be required.
In your view you use:
#model Models.Project.Project
whereas in your controller action you pass:
public ActionResult Create()
{
Riebro.Models.Project.Project proj = new Riebro.Models.Project.Project();
return View(proj);
}
Notice the difference? Models.Project.Project vs Riebro.Models.Project.Project. You don't seem to be using the same type on your controller as on your view.
Also notice that it is bad practice to use namespace names that contain the name of a class.
Another remark is about using the Model keyword in lambda expressions:
#Html.LabelFor(Model => Model.CateogoriesList)
You shouldn't use this keyword. Replace Model with something else.
See your code, what's that? that's the reason cause the error.
<div class="editor-label">#Html.LabelFor( Model => Model.CateogoriesList )</div>
correct one
<div class="editor-label">#Html.LabelFor( Model => Model.CategoryId )</div>
#using (Html.BeginForm())
{
#Html.ValidationSummary(true);
<fieldset>
<legend>Submit Your Request</legend>
<div class="editor-label">#Html.LabelFor(x=>x.CategoryId )</div>
<div class="editor-field">
#Html.DropDownList("Category", new SelectList(Model.CateogoriesList) )
</div>
</fieldset>
<p><input type="submit" value="Send for RFP" /></p>
}
Here is my simulation of your entity. I just add another CategoriesList2 which use to simulate the IQueryable object, but it's still working.
public class Project {
public Project() {
CategoryId = 0;
Name = "";
Description = "";
//Categories = new Dictionary<int, string>();
//Entities _db = new Entities(); //ef4
//CateogoriesList = from c in _db.Categories
// orderby c.Name
// select c.Name;
//IQueryable<string> categoriesList = (new string[] { }).AsQueryable();
CateogoriesList = new string[] { "abc", "def", "hij", "klm" };
CategoriesList2 = (new string[] { "abc", "def", "hij", "klm" }).AsQueryable();
}
public int CategoryId { get; set; }
[Required]
[DataType(DataType.Text)]
[Display(Name = "Project Name")]
public string Name { get; set; }
[Required]
[DataType(DataType.MultilineText)]
[Display(Name = "Project Description")]
public string Description { get; set; }
public string[] CateogoriesList;
public IQueryable<string> CategoriesList2;
}
Here is the view by using the IQueryable categories list
#model MvcApplication3.Models.Project
#using (Html.BeginForm())
{
#Html.ValidationSummary(true);
<fieldset>
<legend>Submit Your Request</legend>
<div class="editor-label">#Html.LabelFor(x=>x.CategoryId )</div>
<div class="editor-field">
#Html.DropDownList("Category", new SelectList(Model.CategoriesList2) )
</div>
</fieldset>
<p><input type="submit" value="Send for RFP" /></p>
}
You are using the reserved keyword Model in your lambda expression
<div class="editor-label">#Html.LabelFor( Model => Model.CateogoriesList )</div>
try this
<div class="editor-label">#Html.LabelFor( m=> m.CateogoriesList )</div>

Resources