Move Html.DropDownListFor into EditorTemplate - asp.net-mvc

Trying to create an editor template using a dropdownlist in MVC4. I can get the dropdownlistfor to work directly in the view as such:
#Html.DropDownListFor(model => model.Item.OwnerId, new SelectList(Model.DDLOptions.CustomerOptions, "Value", "DisplayText"))
But then to "generify" it and put it into an editor template, I cannot get it to work.
Here is what I am trying in my EditorTemplate partial:
#Html.DropDownListFor(model => model, new SelectList(Model.DDLOptions.CustomerOptions, "Value", "DisplayText"))
I am getting the error:
Exception Details: Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: 'int' does not contain a definition for 'DDLOptions'
Model.DDLOptions.CustomerOptions is of type IEnumerable<DDLOptions<int>>:
public class DDLOptions<T>
{
public T Value { get; set; }
public string DisplayText { get; set; }
}
Does this error have something to do with DDLOptions being a generic?

This line is the problem:
#Html.DropDownListFor(model => model, new SelectList(Model.DDLOptions.CustomerOptions, "Value", "DisplayText"))
Your model is simply an int, based on the code above, but then you're calling new SelectList(Model.DDLOptions.CustomerOptions, "Value", "DisplayText") in the partial too, referencing Model.DDLOptions, which does not exist in your model in the editor template. Your model is just an int.
There are several ways to do this, one of which is creating a custom model class for your item owner and have it contain the ownerID and the DDLOptions. Another would be to stick the DDLOptions in the ViewBag, but I usually stay away from that as I prefer using well-written, view-specific view models.
I hope this helps.

Related

MVC EditorFor only rendering single property, not EditorTemplate

Got some issues with EditorFor and I can't understand the behaviour.
I have a list of objects where I want to render an editor template based on that list.
According to https://stackoverflow.com/a/26069912 the EditorFor should render the template for each object.
public class Person
{
public string Name { get; set; }
public string PhoneNumber { get; set; }
}
The template
#model EditForTest.Models.Person
Name
<td>#Html.TextBoxFor(x => x.Name)</td>
Phone
<td>#Html.TextBoxFor(x => x.PhoneNumber)</td>
In the view I got a view model containing a list of persons. When I use
#Html.EditorFor(x => x.Persons)
the template is not rendered and somehow the first property is rendered on the page.
As #StephenMuecke mentioned I had a problem with my editor template.
Once the template was moved to the correct path /Views/Shared/EditorTemplates/Person.cshtml the editor was rendered properly.

How to hide primary key input field using editorfor

Hi I'm using Editorfor() to make a little form that submits view model to the controller. Editorfor() nicely prints input fields of the model but it also prints primary key field. So I want to hide primary key field.
#Html.EditorFor(m=>m.viewmodel)
this is markup that I have.
#Html.HiddenFor(m => m.viewmodel.Id);
#Html.EditorFor(m=>m.viewmodel)
have tried this but does not work. and I wanted to make an approach directly to the model but I'm using EF Designer, so I'm not sure where to begin. Please give me an advice.
Try this:
[HiddenInput(DisplayValue = false)]
public int ProductID { get; set;
Use a custom editor template. For example:
MyViewModel.cshtml (stored in ~/Views/Shared/EditorTemplates folder, structured like a partial view):
#model MyViewModel
#Html.HiddenFor(m => m.Id)
#Html.EditorFor(m => m.Property1)
#Html.TextboxFor(m => m.Property2)
#Html.TextAreaFor(m => m.Property3)
// Whatever else you want in the template
Then you can just call EditorFor on your model in your view that needs to use it and MVC will know to use your custom template:
#Html.EditorFor(m => m.MyViewModel)
To use a custom display template that isn't based on the name of the type, you can also use the [UIHint] attribute as described here: http://www.growingwiththeweb.com/2012/12/aspnet-mvc-display-and-editor-templates.html
Don't create any field for your key attribute. Without a field to check, the validation has nothing to complain about. Later you can supply a value for the primary key in the controller.

An ASP.NET MVC Gotcha? Frustrated

I have an issue with ASP.NET MVC html helpers like TextBoxFor(), HiddenFor(), etc. If I have a model such as Employee with the string member Name and execute Html.TextBoxFor(p => p.Name), is it wrong for me to assume that ASP.NET MVC will always use the value in the Employee's Name? Because it doesn't. ASP.NET will override that binding and use what's in the POST.
For exmaple let's say I have the following code:
Model
namespace MvcApplication2.Models
{
public class Company
{
public string Name { set; get; }
public List<Employee> Employees { set; get; }
}
public class Employee
{
public string Name { set; get; }
}
}
Controller
namespace MvcApplication2.Controllers
{
public class HomeController : Controller
{
public ActionResult Company(string Name)
{
return View(new Company {
Name = Name,
Employees = new List<Employee> {
new Employee { Name = "Ralph" },
new Employee { Name = "Joe" } }
});
}
}
}
Home/Company.cshtml
#using MvcApplication2.Models;
#model Company
<h2>Company's Name: #Model.Name</h2>
#foreach (Employee emp in Model.Employees)
{
Html.RenderPartial("Employee", emp);
}
Home/Employee.cshtml
#model MvcApplication2.Models.Employee
<b>Employee's Name: </b> #Html.TextBoxFor(p => p.Name);
When I hit the relative url "Home/Company?Name=MSFT", I expected Employee.cshtml to render "Ralph" and "Joe" in the textboxes but instead it renders MSFT for both textboxes. What do I have to do so that "Ralph" and "Joe" shows up in the textboxes? Do i have to make sure that my POST and GET variables never conflict in all layers of my view models (in this case the Company and Employee classes)? This seems silly. There's got to be an easy workaround, right?
Here's a screenshot of the result:
HtmlHelper methods and ModelState
An important thing to understand with MVC HtmlHelper methods:
They always look at ModelState first, value second, ViewData third.
The ModelState is important, because it contains the user-submitted values. If a page fails validation, the ModelState is used to store the previous values and error messages.
If you want to POST a form, and if everything is valid, you want to show the form again, you either have to:
Redierect from the POST to the GET (following the PRG Pattern)
Clear the ModelState: ModelState.Clear();
EditorFor vs RenderPartial
Another important thing to mention is the difference between EditorFor/DisplayFor vs RenderPartial.
When you use EditorFor/DisplayFor for an item (such as .EditorFor(m => m.Person)), it adds a sort-of "namespace" to the template of "Person" so that the sub-controls will have a unique name. For example, in the template, .TextBoxFor(p => p.Name) will render something like <input name="Person.Name" ....
However, when you use RenderPartial, no such namespace is created. Therefore, .TextBoxFor(p => p.Name) will render <input name="Name" ..., which will not be unique if you have multiple editors.
I understand that this behavior might be frustrating, and that it might seem illogical. However, your frustration is a result of not fully understanding how Model binding works. There is a logical chain of events that occur during binding, and if you understand how it works then it will help avoid these kinds of mistakes.
As Scott mentioned, MVC will always look in ModelState first. You can avoid this problem by making sure you don't have any querystring parameters that have the same name as any properties in your model.

Trouble getting DropDownListFor to work on EditorFor in MVC3

I've got a pretty simple problem that has a solution I'm not able to find.
Given the following model:
public class InfoViewModel
{
public SelectList States { get; set; }
[DisplayName("State")]
public string State { get; set; }
}
The following works fine in my view:
#Html.DropDownListFor(m => m.State, Model.States)
However, if I try to pull this into an editor template (named "SelectList"), such as:
#model System.String
#Html.DropDownListFor(m => m, ViewData["selectList"])
And then use EditorFor to build the dropdown list:
#Html.EditorFor(m => m.State, "SelectList", new { selectList = Model.States })
It fails with the following error:
'System.Web.Mvc.HtmlHelper<string>' does not contain a definition for
'DropDownListFor' and the best extension method overload
'System.Web.Mvc.Html.SelectExtensions.DropDownListFor<TModel,TProperty>
(System.Web.Mvc.HtmlHelper<TModel>,
System.Linq.Expressions.Expression<System.Func<TModel,TProperty>>,
System.Collections.Generic.IEnumerable<System.Web.Mvc.SelectListItem>)'
has some invalid arguments
I'm having a hard time understanding the difference between these two. I've tried various workarounds to troubleshoot, and either get the same error, or something else.
Thanks in advance.
There is no such overload:
#Html.DropDownListFor(m => m, ViewData["selectList"])
The second parameter of the DropDownListFor helper musty be a SelectList. So:
#Html.DropDownListFor(m => m, (SelectList)ViewData["selectList"])

MVC 3 - Scaffolding drop down list

I am playing with the Scaffolding that is in Asp.net mvc
I have a property on my view model for countries
public IEnumerable<SelectListItem> Countries { get; set; }
Yet, when I create a view and specify the viewmodel it doesn't scaffold a drop down list as I expected it to. In fact it get completely ignored.
I have compiled the project before doing it
I also tried adding a property like this
public int CountryId { get; set; }
As this article suggested there is a convention at work
http://blog.stevensanderson.com/2011/01/28/mvcscaffolding-one-to-many-relationships/
I am using the Add view option you have when right clicking in a controller action method
Any ideas what is wrong?
In my current project, i faced this problem and couldn't find a quick way to scaffold the Dropdown list of a one-many relation inside one of my Entities.
What I ended up doing was like the following:
1- Create the normal AddView=>Create way.
2- If i had a ID property in my model class, the defaul;t template will generate something like this to represent this property in my view:
<div class="editor-label">
#Html.LabelFor(model => model.CityID)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.CityID)
#Html.ValidationMessageFor(model => model.CityID)
</div>
3- Now I need to replace this default template with a working one, so I wrote this code in the CREATE method:
IEnumerable<SelectListItem> cityItems = dataContext.Cities.AsEnumerable().Select(c => new SelectListItem()
{
Text = c.Name,
Value = c.ID.ToString(),
Selected = false,
});
SelectList cityList = new SelectList(cityItems, "Value", "Text");
ViewBag.CityList = cityList;
this will fetch the Cities table and create a Selectlist that i can pass to my view and work with it to provide the DrobDown with it's items.
4- replace the default code in my View by one like the following:
<div class="Post-label">
<div class="editor-label">
#Html.LabelFor(model => model.CityID)
</div>
<div class="editor-field">
#Html.DropDownListFor(m => m.CityID, ViewData["CityList"] as SelectList)
#Html.ValidationMessageFor(model => model.CityID)
</div>
</div>
The reason I've used ViewData["CityList"] instead of ViewBag.CityList is that this one worked but the other not.
5- now my view is working find and is fetching the City data just like what I expected, and using the same model inside my Edit view resulted in a working one too.
Give it a try and let me know what happened, Thanks.
I have noticed that for a given model, the "Create" scaffold-generated code when creating a new controller is different than if you right-click in an existing controller and say "Add View" and choose the "Create" scaffolding template. In the first case, given you have the correct properties on the child class
public Country Country {get;set;}
public int CountryID {get;set;}
then this case (adding controller with MVC scaffolding read/write and appropriate Model class) WILL generate a #Html.DropDownList for the parent relationship, whereas right-clicking within the controller Create method will not scaffold the drop-down but will instead create an #Html.EditorFor for the relationship.
So the answer if you want scaffolding code to generate the drop-down may be to delete and re-create your controller if possible, otherwise manually add in the appropriate code.
In order to have the option to choose a country with a dropdown the property in your Model should be:
public Country Country{ get; set; } Navigation property used by EF, doesn't involve the database
with
public Country CountryId{ get; set; }
Create the foreign key on the Person table
Each instance/record of a person is associated with a country: the relation is defined with the navigation property via code and with the CountryID for the database.
The scaffholding template will then generate the edit/create methods and views using :
ViewBag.PossibleCountries
Here's a similar scenario.

Resources