DropDownListFor not selecting value of view model field - c#

Not sure what I'm doing wrong, but my dropdown doesn't want to select the value I want it to select. I have the following
Controller actions
// GET: /Contract/Create
public ActionResult Create()
{
var model = new ContractViewModel();
var authors = _authorService.GetAuthors();
var publishers = _publisherService.GetPublishers();
model.AuthorsList = new SelectList(authors, "AuthorID", "Name", authors.First());
model.PublishersList = new SelectList(publishers, "PublisherID", "Name", publishers.First());
return View(model);
}
// POST: /Contract/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(ContractViewModel contractViewModel)
{
if (ModelState.IsValid)
{
Contract contract = new Contract();
contract.CanUsePublisherPartners = contractViewModel.CanUsePublisherPartners;
contract.Author.AuthorID = Convert.ToInt32(contractViewModel.SelectedAuthorID);
contract.Publisher.PublisherID = Convert.ToInt32(contractViewModel.SelectedPublisherID);
var success = _contractService.AddContract(contract);
if (success)
{
return RedirectToAction("Index");
}
}
contractViewModel.AuthorsList = new SelectList(_authorService.GetAuthors(), "AuthorID", "Name");
contractViewModel.PublishersList = new SelectList(_publisherService.GetPublishers(), "PublisherID", "Name");
ViewBag.ErrorMessage = "An error occured when trying to add the Contract. A contract between this Author and Publisher may already exist! Please try again and if the problem persists, contact the Sys Admin.";
return View(contractViewModel);
}
ViewModel
public class ContractViewModel
{
[Display(Name = "Can the author distribute through the publisher's partners?")]
public bool CanUsePublisherPartners { get; set; }
[Display(Name="Author")]
public int? SelectedAuthorID { get; set; }
[Display(Name = "Publisher")]
public int? SelectedPublisherID { get; set; }
public SelectList AuthorsList { get; set; }
public SelectList PublishersList { get; set; }
}
View binding of drop down lists
<div class="form-group">
#Html.LabelFor(model => model.SelectedAuthorID, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.HiddenFor(m => m.SelectedAuthorID)
#Html.DropDownListFor(m => m.SelectedAuthorID, Model.AuthorsList)
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.SelectedPublisherID, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.HiddenFor(m => m.SelectedPublisherID)
#Html.DropDownListFor(m => m.SelectedPublisherID, Model.PublishersList)
</div>
</div>
What's the problem?
When I submit my form, the values of SelectedAuthorID and SelectedPublisherID are the default of int - 0.
I am really at wits end here, I looked at some details trying to find out if they impact anything. E.g. some people had trouble when the Selected container property had the same name as the value property of the list items and so forth.
If anyone has any suggestions would be great to share them!

I believe the issue is that you have the SelectedPublisherID and SelectedAuthorID on the page twice.
The Html.HiddenFor(m => m.SelectedAuthorID) should not be necessary alongside the DropDownListFor.
A minor thing, general C# naming conventions use PascalCase, which means that the properties should be named SelectedAuthorId instead of ID.

Related

How to populate a dropdownlist based on Id from a SQL Database

I am trying to create a drop down list in one of my CRUD screens that are based on a different model in my program. Right now it inputs the values based on ID but I want it to be able to populate the names of instructors in a drop-down list.
I've tried a few different things using the select list but it didn't seem to work.
This is my model:
[Table("Section")]
public class Section
{
[Key]
public int Id { get; set; }
[DisplayName("Section")]
public int? section { get; set; }
public Nullable <int> instructor_id { get; set; }
public int location_id { get; set; }
public int modality_id { get; set; }
public int DOW_id { get; set; }
public int course_id { get; set; }
public virtual DOW DOW { get; set; }
public virtual Instructor Instructor { get; set; }
public virtual Location Location { get; set; }
public virtual Modality Modality { get; set; }
public virtual Course Course { get; set; }
This is my controller:
// GET: Sections/Create
public ActionResult Create()
{
return View();
}
// POST: Sections/Create
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see https://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Id,section,startTime,endTime,startDate,endDate,isTap,isActive,instructor_id,location_id,modality_id,DOW_id,course_id")] Section section)
{
if (ModelState.IsValid)
{
db.Sections.Add(section);
db.SaveChanges();
return RedirectToAction("Index");
}
//ViewBag.instruct = new SelectList(db.Instructors, "Id", "lname", section.instructor_id);
return View(section);
}
And this is my view
<div class="form-group">
#Html.LabelFor(model => model.instructor_id, "instructor_id" , htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.instructor_id, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.instructor_id, "", new { #class = "text-danger" })
</div>
</div>
You need to change the Get Method in Controller as follows:
// GET: Sections/Create
public ActionResult Create()
{
ViewBag.Instructors = db.Instructors.ToList();
return View();
}
Your HTML need to be changed as follows:
<div class="form-group">
#Html.LabelFor(model => model.instructor_id, "instructor_id" , htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.instructor_id, new SelectList(ViewBag.Instructors, "Id", "Name"), new { #class = "form-control" } )
#Html.ValidationMessageFor(model => model.instructor_id, "", new { #class = "text-danger" })
</div>
</div>
Make sure field names are correct.
Taken form John Peters answers, kindly take a look
Create a data layer that retrieves a list of what you want. Then use EF to get all the states.
//assuming you have a table of states..
var states = db.States();
The states table should be a Unique list of states.
var selectList = new List<SelectListItem>();
foreach(var thing in states){
//if you got everything, thus the ID field for the value...
selectList.Add(new SelectListItem {Text =thing.State, Selected = false, Value = thing.ID);
}
Make sure in your Viewmodel class that selectlist is a public property.....and set to what you did above. You also need to provided a string for the view selection post back.
StatesSelectList = selectList;
public IEnumerable<SelectListItem> StatesSelectList {get;set;}
public string SelectedState {get;set;}
In your view, do this:
#Html.DropDownListFor(p => Model.SelectedState, Model.StatesSelectList)

How can I pass list of strongly type objects from controller to a dropdown on a view?

I would like to pass list of strongly typed object to a dropdown which is located on my view.
Usually to achieve this I used ViewBags like in following example:
public ActionResult ChooseLevel()
{
List<Levels> LevelList = GetAllLevels();
ViewBag.LevelList = LevelList
var model = new Levels();
return View(model);
}
And I would simply write this on a view, and I would get all my levels listed there:
<div class="form-group">
#Html.LabelFor(model => model.LevelId, new { #class = "control-label col-md-3 col-sm-3" })
<div class="col-md-9 col-sm-9">
#Html.DropDownListFor(model => model.LevelId, new SelectList(ViewBag.LevelList, "LevelId", "LevelName"), "", new { #class = "form-control" })
</div>
</div>
But now I'm wondering can I simply pass my list of Levels there, and choose them from dropdown list, without storing them to a viewbag first?
For example :
public ActionResult ChooseLevel()
{
List<Levels> LevelList = GetAllLevels();
return View(LevelList);
}
On a view I would accept multiple items by writing IEnumerable on a view:
#model IEnumerable<Levels>
and after that I could somehow choose only one item and post it back to a server?
How can I solve that issue?
You need to add this List to your existing Model or View Model:
class ModelName
{
public virtual IEnumerable<SelectListItem> lstTypes { get; set; }
public virtual int intTypeId { get; set; }
//Other existing properties here
}
On your Controller, you can now add this list to your Model before you return to your view:
ModelName objModel = new ModelName();
List<Levels> LevelList = GetAllLevels();
objModel.lstTypes = LevelList.Select(y => new SelectListItem()
{
Value = y.LevelId.ToString(),
Text = y.LevelName.ToString()
});
return View(objModel);
Then you can now display it on your view:
#model ModelName
//First parameter will be the Id that will be selected by your user when they post it
//Second parameter will be the enumerable list of dropdown
//Third parameter is the default option which is optional, and the last is the HTML attributes
#Html.DropDownListFor(c => c.intTypeId, Model.lstTypes , "Please select an item", new { #class = "form-control" })
You can create new viewmodel that contains multiple models (old model and LevelList model). like this:
public class newViewModel
{
public IEnumerable<level> levels{ get; set;}
public OldModel oldModel {get; set;}
}
Model class
public class TestViewModel
{
public List<SelectListItem> EnterpriseList { get; set; }
}
Controller:
var model = new TestViewModel() {
EnterpriseList = EnterpriseData.Select(p=>new SelectListItem() { Value = p.Value,Text = p.Name}).ToList()
};
return View(model);
View:
#Html.DropDownListFor(p => p.Enterprise, Model.EnterpriseList, "Please select a", new { #class = "form-control", #style = "height: auto" })

Asp.net MVC multiple select for List property

I'm fairly new to ASP.Net MVC so forgive me for anything that should just be obvious.
I have an object that contains a property that is a list. I only don't know how I should implement this in the create.
this is the object:
public class TeamMember
{
public int TeamMemberId { get; set; }
public string FristName { get; set; }
public string LastName { get; set; }
public DateTime BirthDate { get; set; }
public string Biographie { get; set; }
public virtual Image Image { get; set; }
public virtual List<DanGrade> DanGrades { get; set; }
}
In the create view I want to be able to select multiple Dangrades.
I tried to modify an editor Template for it that looks like this:
#using BudoschoolTonNeuhaus.Models
#model BudoschoolTonNeuhaus.Models.TeamMember
#{
var db = new ApplicationDbContext();
var danGrades = db.DanGrades.ToList();
}
<select multiple name="#ViewData.TemplateInfo.HtmlFieldPrefix" class="dropdown">
#foreach (var dan in danGrades)
{
<option value="#">
#dan.DanGradeId: #dan.BudoSport, #dan.Grade
</option>
}
</select>
but this does not give the result that I thought it would, its just showing mutiple dangrade labels in the create view that you can see here:
#model BudoschoolTonNeuhaus.Models.TeamMember
#{
ViewBag.Title = "Create";
Layout = "~/Views/Shared/_Admin_Layout.cshtml";
}
<div class="wrapper">
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>TeamMember</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.FristName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.FristName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.FristName, "", new { #class = "text-danger" })
</div>
</div>
.... // controls for other properties of model
<div class="form-group">
#Html.LabelFor(model => model.DanGrades, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.DanGrades, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.DanGrades, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Image, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
<input type="file" id="Image" name="Image" hidden />
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
</div>
current HTML output:
Thanks for you help in advance!
To create a <select multiple> you use the ListBoxFor() method in your view.
But your model needs two properties to generate a listbox, a IEnumerable<int> to bind the selected values to (assumes the ID proeprty of DanGrade is typeof int), and an IEnumerable<SelectListItem> to display the <option> elements.
You editing data, so always start with a view model
public class TeamMemberVM
{
public int? TeamMemberId { get; set; }
....
[Display(Name = "DanGrades")]
public IEnumerable<int> SelectedDanGrades { get; set; }
public IEnumerable<SelectListItem> DanGradesList { get; set; }
}
and your view will be
#model yourAssembly.TeamMemberVM
....
#Html.ListBoxFor(m => m.SelectedDanGrades, Model.DanGradesList, new { #class="dropdown" })
and your controller methods will be
public ActionResult Create()
{
TeamMemberVM model = new TeamMemberVM();
ConfigureViewModel(model);
// For an Edit method, your would set the existing selected items here
model.SelectedDanGrades = ...
return View(model);
}
public ActionResult Create(TeamMemberVM model)
{
if (!ModelState.IsValid)
{
ConfigureViewModel(model); // repopulate the SelectList
return View(model);
}
// model.SelectedDanGrades contains the ID's of the selected options
// Initialize an instance of your data model, set its properties based on the view model
// Save and redirect
}
private void ConfigureViewModel(TeamMemberVM model)
{
IEnumerable<DanGrade> danGrades = db.DanGrades();
model.DanGradesList = danGrades.Select(x => new SelectListItem
{
Value = x.DanGradeId.ToString(),
Text = x.??? // the name of the property you want to use for the display text
});
}
Note also that your view has a file input so your view model needs a HttpPostedFileBase property to bind the file to
public HttpPostedFileBase Image { get; set; }
and in the view
#Html.TextBoxFor(m => m.Image, { new type ="file" })
Shouldn't your model be like that ?
[UIHint("NameOfTheEditorTemplate")]
public virtual List<DanGrade> DanGrades { get; set; }
Be sure to put the EditorTemplate under one of these two paths
~/Views/Shared/EditorTemplates
~/Views/Controller_Name/EditorTemplates
As explained in this post
So you are trying to save a list of custom objects inside your object. First of all, know that if you try to save teammember to a database your list of objects will not save. I've experienced this same issue and its needs some special configuring to get just that to work.
Second you can't select custom objects from a < select >. Select returns string[] to your controller. So objects, no. You can't return complex items like that using select directly.
What you can do is return a string[] and use the individual strings (maybe it contains name, maybe it contains id?) and then use that array to pull each object to your teammember object in the controller from the dangrade db context (I'm assuming that is where they are stored).
So for example if you Go back to your controller and add (string[] dangrades) to your parameters. Your parameters now looks something like this (string[] dangrades, Bind[blahblah] ... teammember).
Now after referencing the other database you can do as follows
teammember.Dangrades = new list<Dangrade>();
foreach(string item in dangrades)
{
var dangradeselected = from x in db.dangrades where x.name = item select x;
var dangradefromlinq = dangradeselected.tolist();
teammember.Dangrades.Add(dangradefromlinq[0]);
}
If you had previously stored dangrades in some other format (ie not a database) then you will have to append your code, or ask specifically with that for a better answer.
Also don't forget to give your select and id= (lookup html attributes) so that the controller can recognize it.
You can probably make this (pseudo)code a little neater. Also don't forget about possible null values.
If you want to save a list of items for each teamember you can also look into having 2 databases. I'm not sure if this is recommended. But you can have one for teammembers, and one for dangrades. In the case of dangrades you would add an additional property called grouping id that would match the id of your teammember. So when you pull up your teammember you could also pull up all related dawngrades that match its database id.
That's everything I can think of. If you find a simpler solution by all means go with that.

Where should I set size of array for Razor?

I am trying to make a Create page for my model object. Object which I am trying to create have "one to many" relationship. So for now I am trying to add multiple fields for it. I have found this answer:
MVC "create view" when there is one to many relationship in model
The problem is that I should have at least 10 additional fields. Or in the best case I should have possibility to set their amount manually. So I decided to use an array. But in C# I can't set it's size in model because arrays are dynamic.
SO THE QUESTION IS:
Where can I set size of array so the Razor will know how many fields to create.
Here are my models:
public class OrderCreateView
{
//Other fields
public ComponentOfOrder[] ComponentOfOrders { get; set; }
}
public class ComponentOfOrder
{
public string NameOfComponentOfOrder { get; set; }
}
Here are my Controller's methods for Create page:
public ActionResult Create()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "anotherStuff,ComponentOfOrders")] OrderCreateView orderCreateView)
{
if (ModelState.IsValid)
{
Order order = new Order
{
//Another fields
};
db.Orders.Add(order);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(orderCreateView);
}
My Razor page:
#model Safronov.Models.OrderCreateView
#{
ViewBag.Title = "Create";
//Model.ComponentOfOrders = new Safronov.Models.OrdersDB.ComponentOfOrder[10];
//here it doesn't work with error message "Object reference does not point to an instance of an object"
}
some stuff on page
#foreach (var component in Model.ComponentOfOrders)
{
<div class="form-group">
#Html.LabelFor(x => component.NameOfComponentOfOrder, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(x => component.NameOfComponentOfOrder, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(x => component.NameOfComponentOfOrder, "", new { #class = "text-danger" })
</div>
</div>
}
You can use a List of ComponentOfOrder.
List<ComponentOfOrder> mymodel=new List<ComponentOfOrder>();
and use by this way :
mymodel[0].NameOfComponentOfOrder
this is ur problem ?

EditorTemplate for a productcategory (foreign key property)

Hello I am trying to create an editortemplate for a productcategory.
The productcategorie class is:
public class ProductCategorie
{
public int Id { get; set; }
public int Gewicht { get; set; }
public string Naam { get; set; }
}
The Product class is:
public class Product
{
public int Id { get; set; }
...
public ProductCategorie Categorie { get; set; }
}
Now in the edit view that I auto created(scaffolding) I got:
<div class="form-group">
#Html.LabelFor(model => model.Categorie, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Categorie)
#Html.ValidationMessageFor(model => model.Categorie)
</div>
</div>
As you can see I use the editorFor so I can then use a EditorTemplate for it!
But it crashes since it somehow doesn t give it a model.(it gives a NullReferenceException on the Model, the model is null, but I need it so I can know what SelectListItem has to have True at Selected!)
Here is the probleming ProductCategorie.cshtml file that is in the EditorTemplates
#using Foo.Data
#using Foo.Models
#model ProductCategorie
#{
var items = new List<SelectListItem>();
using (var context = new FooDbContext())
{
List<ProductCategorie> categorielijst = context.ProductCategorieen.ToList();
categorielijst.ForEach(x => items.Add(new SelectListItem { Text = x.Naam, Value = x.Id.ToString(), Selected = x.Id == ((ProductCategorie)Model).Id }));
}
}
#Html.DropDownList("", items)
So basicly I am asking how can I get the Model or the data of the current selected item when editing an item that has a property that is actualy a foreign key item.
If you got any questions feel free to ask.
(I use EF 6 code first, ASP.Net MVC 5)
We solved this in SO chat.
Basically, the Model.Categorie property was null.. hence why the EditorTemplate had null passed into it.
Glad to help :)

Categories

Resources