MVC 3 - Scaffolding drop down list - asp.net-mvc

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.

Related

How to validate against a Model's metadata through a custom ViewModel?

I'm working on a webapp using ASP MVC. I'm building a page to edit a user's data (model USER, view ModifyUser).
I have a model with validations in this manner:
[MetadataType(typeof(USERS_Metadata))]
public partial class USER
{
public class USERS_Metadata
{
[Required(ErrorMessage = "FALTA NOMBRE")]
[StringLength(30, ErrorMessage = "Nombre entre 3 y 30 caracteres", MinimumLength = 3)]
[RegularExpression(#"^[a-zA-Z''-'\s]{1,40}$", ErrorMessage = "Error en el formato del nombre.")]
public string NAME { get; set; }
I then use a view that automagically validates user inputs:
<div class="editor-label">
<%: Html.LabelFor(model => model.SURNAME) %>
</div>
<div class="editor-field">
<%: Html.EditorFor(model => model.SURNAME) %>
<%: Html.ValidationMessageFor(model => model.SURNAME) %>
</div>
The problem is my view is also gonna need to access some other entities with their own models, like USERCATEGORY, which makes using an strongly typed view a bit more uncomfortable.
Aditionally, and may be even more important, I don't want my view to have to deal with, and even knowing about, properties such as the user's session ID, which currently I handle like this (and i hate it):
<%: Html.HiddenFor(model => model.SESSIONID) %>
Unless I'm utterly mistaken, the most sane option is to build a custom ViewModel. I decided to build my ModifyUserViewModel to match those fields in USER I'm gonna need in my view, and add a few fields from the other models... But I have no idea of how to propagate the metadata in USER, that I use for field validation, to the new ViewModel. This metadata is automatically built from the database, and copypasting it would make me feel dead inside, even if it works.
What is the canonical, most maintenable, easiest way to validate from the View like I am currently doing, but with a ViewModel?
Try to build your view model as an aggregate of several domain models:
public class MyViewModel {
public USER User { get; set; }
public USERCATETORY Category { get; set; }
}
See a great article here.

How do you load the Viewbag with only items that are set to "Available"?

I have my program with the following model:
public class PlayToy {
public int PlayToyID {get;set;}
public string Name {get;set;}
public bool Available {get;set;}
}
In my controller I have:
Viewbag.PlayToyID = new SelectList(db.PlayToys, "PlayToyID", "Name");
Here is the View
<div class="editor-label">
#Html.LabelFor(model => model.PlayToy
</div>
<div class="editor-field">
#Html.DropDownList("PlayToyID", "Choose a Toy")
#Html.ValidationMessageFor(model => model.PlayToyID)
</div>
This works fine for showing dropdown list of all the Toys on the list, but I would like
to only show the the Items that are 'Available' to play with.
I am new to MVC, so I don't know AJAX or any other Scripting languages to integrate into MVC. So I was wondering how to do this with using just MVC, and would the code to filter the list go in the controller or the view?
Use LINQ to filter your list using the Where() operator
Viewbag.PlayToyID = newSelectList(db.PlayToys.Where(x=>x.Available).ToList(), "PlayToyID", "Name");
I recommend reading up a little on LINQ and how you can apply it to Lists and use in things like Entity Framework. It is an extremely powerful tool in the .NET toolkit for filtering, ordering, grouping, sorting, etc.

Pass an entire model on form submission

I understand that I can use #Html.HiddenFor(m => m.parameter) and when the form is submitted, that parameter will be passed to the controller. My model has many properties.
Is there a shorter way of passing the entire model at once to the controller or must I do it one by one each time?
The model will be passed to the controller in its entirety, but the values of properties that are not bound by input or hidden fields will be lost.
You have to either bind the properties in the form on the client-side, or re-fetch the entity on the server-side.
You seem to be asking for something like #Html.HiddenFor(m => m.Model), and that is not possible. Sorry
One thing to keep in mind, if you have tons of hidden fields, you may be sending more data to the view than you really need. Consider employing view models
For anyone else who looks at this you can do a #Html.EditorForModel() in a hidden div. You'd also have to use #Html.EditorFor(model => model.ObjectProperty) for each object property of the model.
<div hidden="hidden">
#Html.EditorForModel()
#Html.EditorFor(model => model.ObjectProperty)
#Html.EditorFor(model => model.ListOfObjectsProperty)
</div>
The entire model will be posted if you are using a FORM element. Your elements using the Model obviously need to be inside the form element
You can also POST the form yourself say by using JQuery
See this other stack issue for that : jQuery AJAX submit form
Have a close look at the anwser by "Alfrekjv"
This is already built in. Consider this model:
public class MyModel
{
public string PropertyA { get; set; }
public string parameter { get; set; }
}
and now consider this action:
[HttpPost]
public ActionResult PostSomeData(MyModel model)
{
}
MVC will leverage the FormCollection and fill in the MyModel class where it can. If you don't have the PropertyA in the form then it will be null. But since you have an input for the parameter property it will be filled in.
You can check only the properties you want:
if (this.ModelState.IsValidField("Name"))
{
// .....
}
instead of:
if (this.ModelState.IsValid)
{
// .....
}
#using (Ajax.BeginForm("my_function", "my_controller", new AjaxOptions { InsertionMode = InsertionMode.Replace }, mymodel))

Move Html.DropDownListFor into EditorTemplate

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.

Why does my MVC dropdownlist have a value selected despite the property value being NULL?

I have a dropdownlist where a user can select a care provider. In the database the value for this is null however in the application, a value is selected. What do I need to do to have the null value appear as a blank? I thought this was the default behavior. I changed to using strongly typed lists in my view model instead of the viewbag and this may have broken at that time.
Here is the view markup:
<div class="editor-label">
#Html.LabelFor(model => model.PsychologistId, "Psychologist")
</div>
<div class="editor-field">
#Html.DropDownListFor(x => x.PsychologistId, Model.ListPsychologists)
#Html.ValidationMessageFor(model => model.PsychologistId)
</div>
Here is the property from the view model:
[DisplayName("Psychologist")]
public Nullable<int> PsychologistId { get; set; }
Here is the relevant part of my controller:
model.ListPsychologists = new SelectList(XXX, "Id", "DisplayName");
return this.View(model);
where XXX is just the LINQ expression with filtering and sorting criteria. It's been omitted for clarity and space.
The model passed from the controller to the view has PsychologistId being null. And the SelectedValue property on model.ListPsychologists is null.
if PsychologistId is an int, it will assign 0 value to it since int is not a nullable type.
Show your model and controller if my assumption above is not true.
Does your SelectList contain entry with Id==null? I doubt that. And that means that you will have no select list entry matching your value, so browser will select first one available.
Solution: explicitly add entry with Id = null to your SelectList.
I've had these issues before, and what I did to fix it was send the base collection of what I want to fill the dropdown with in the viewmodel, instead of a SelectList. So:
model.ListPsychologists = XXX;
Then in the view:
#Html.DropDownListFor(x => x.PsychologistId, new SelectList(Model.ListPsychologists, "Id", "DisplayName", Model.PsychologistId))
Please verify the SelectList constructor overload I used in MSDN, I'm typing this from memory. Basically you give it the collection of items to use in the SelectList, then the name of the text and value properties (just like you did in your action method code), and the fourth parameter is the value to set the drop down to.
Edit: Also, there is an overload for the DropDownListFor to add a default item in the menu. Like, "Select One".

Resources