MVC Dropdown option displayed as 'null' in debug mode - c#

Afternoon Folks,
Im new to MVC 5 and C# and have a simple form with several fields and a dropdown box. I have used the CRUD method for entity framework and can successfully view and create new records in the system.
The only issue that I have is I have a 'Title' dropdown that links to the entity framework and populates these titles into a list. when I load my web page I can now see the titles available in the dropdown list, but when submitting the form, all the values but the 'Title' field are submitted into the database.
When I debug my program, this field regardless of what I select displays null.
I have followed the following tutorial to get this wo work and looked around the net but im struggeling to find a solution.
Link to tutorial
I have two tables in my model, one named 'Title' and the other named 'Client Record'.
As I have used the database first approach and not code first I have combined these two database models into one:
namespace EDT_Test.Models.ViewModels
{
public partial class Marie_Testing
{
[Display(Name = "Client Ref:")]
public int id { get; set; }
[Display(Name = "Created By:")]
public string CreatedBy { get; set; }
public List<Title> allTitles { get; set; }
[Required]
[Display(Name = "Surname:")]
public string Surname { get; set; }
[Display(Name = "Additional Surname:")]
public string Surname2 { get; set; }
[Required]
[Display(Name = "Forename:")]
public string Forename1 { get; set; }
[Display(Name = "Additional Forename:")]
public string Forename2 { get; set; }
The generated Entity Framework model looks like this:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated from a template.
//
// Manual changes to this file may cause unexpected behavior in your application.
// Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace EDT_Test.Models
{
using System;
using System.Collections.Generic;
public partial class ClientRecord
{
public int id { get; set; }
public string CreatedBy { get; set; }
public string Title { get; set; }
public string Surname { get; set; }
public string Surname2 { get; set; }
public string Forename1 { get; set; }
public string Forename2 { get; set; }
}
}
The only difference for the Title field between the auto created model and Marie_Testing model is I have changed the Title field from a string to a list item.
My Create.cshtml holds a div for the Title dropdown that looks like this (This links to my model named Marie_Testing and not the auto generated ones created by the Entity Framework:
<div class="form-group">
#Html.LabelFor(model => model.allTitles, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
<select id="titleid" name="titlename" class="form-control">
#foreach (var item in Model.allTitles)
{
<option value="#item.id">#item.Title1</option>
}
</select>
</div>
</div>
My code for the ClientRecordsController is:
// GET: ClientRecords/Create
public ActionResult Create()
{
////set the defaults (dropdown) of the page for the ceaton of a new record.
Marie_Testing vmPopulateData = new Marie_Testing();
List<Title> titles = (from t in db.Titles select t).ToList();
//List<Title> titles = Title.Select(t => new{t.id, t.Title}.ToString.ToList());
vmPopulateData.allTitles = titles;
return View(vmPopulateData);
}
// POST: ClientRecords/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "id,CreatedBy,Title,Surname,Surname2,Forename1,Forename2")] ClientRecord clientRecord)
{
if (ModelState.IsValid)
{
db.ClientRecords.Add(clientRecord);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(clientRecord);
}
Any help is much appreciated as I don't understand how can see the dropdown list in the web page but cannot seem to grab the selected value and post this to the database.
Regards
Betty B

Why not try the #Html.DropDownListFor?
Instead of this:
<select id="titleid" name="titlename" class="form-control">
#foreach (var item in Model.allTitles)
{
<option value="#item.id">#item.Title1</option>
}
</select>
try:
#Html.DropDownListFor(x => x.PropertyToBindTo, new SelectList(Model.allTitles, "id", "Title1"), new { #class = "form-control", id = "Title", name = "Title" })
Where x.PropertyToBindTo is whatever value that you need to get from that select list. Try it out... you may have to play with it a little in order to really understand how you need to work it.

You need to have a string field to hold the value of the selected from the dropdown so your view would change from
<select id="titleid" name="titlename" class="form-control">
to
<select id="Title" name="Title" class="form-control">
And you will also have a Title property on your view model like this
public string Title{get;set;}
You need to have a read on how MVC binds forms to models to get an understanding of why this happens.
Hope this helps..

well I prefer to use this
C#
List<Titles> oTitlesList = TitlesService.getTitles();
ViewBag.DropDownListBag = new SelectList(oTitlesList , "Id", "Name");
Razor
#Html.DropDownListFor(model => model.yourModelAtt, #ViewBag.DropDownListBag as SelectList, "Select.....", new { #class = "form-control" })

Related

How to Allow Null input for C# Razor page SelectList

Considering the following data model:
public partial class RootEntity
{
[Key]
public long Id { get; set; }
[Required(AllowEmptyStrings = false)]
public string Name { get; set; }
public string ThingsAboutIt { get; set; }
public long? RelatedEntity_Id { get; set; }
[ForeignKey("RelatedEntity_Id")]
public RelatedEntity RelatedEntity { get; set; }
}
public partial class RelatedEntity
{
[Key]
public long Id { get; set; }
[Required(AllowEmptyStrings = false)]
public string Name { get; set; }
public ICollection<RootEntity> RootEntities { get; set; }
}
public partial class MyContext : IdentityDbContext
{
public MyContext(DbContextOptions<MyContext> options) : base(options) { }
public virtual DbSet<RootEntity> RootEntities { get; set; }
public virtual DbSet<RelatedEntity> RelatedEntities { get; set; }
}
When using the EF (Core) scaffolded Insert page in a Razor page application, the default pattern used for selecting singl-y linked entity records is:
<div class="form-group">
<label asp-for="RootEntity.RelatedEntity_Id" class="control-label"></label>
<select asp-for="RootEntity.RelatedEntity_Id" class ="form-control" asp-items="ViewBag.RelatedEntity_Id"></select>
</div>
and the initialization of this looks something like this:
ViewData["RelatedEntity_Id"] = new SelectList(_context.RelatedEntities, "Id", "Name");
I can configure this a tiny bit, by changing that third parameter to something like "Name" - if such a property exists.
I need way more configurabililty - I need at least null values to be selected.
In an attempt to allow nulls, I use this pattern:
List<SelectListItem> selectList = _context.RelatedEntities
.AsNoTracking()
.Select(x => new SelectListItem()
{
Value = x.Id.ToString(),
Text = x.Name
})
.ToList();
selectList.Insert(0, new SelectListItem()
{
Value = null, // <=========== Here lies the problem...
Text = "--- Select Related Entity ---"
});
ViewData["RelatedEntity_Id"] = selectList;
The rendered HTML looks something like this:
<select class="form-control" id="RootEntity_RelatedEntity_Id" name="RootEntity.RelatedEntity_Id">
<option selected="selected">--- select Related Entity ---</option>
<option value="3">FOO</option>
<option value="2">MMMBAR</option>
</select>
The problem I'm having is that the value "--- Select Related Entity ---" becomes bound to that model property. This makes it fail validation. (ModelState.IsValid == false)
I have tried changing the SelectListItem value to a blank string, "-1", but none of it fixes the model validation error.
I've also tried to build a custom binder for entity classes, but this binds the root model itself to it - which obviously does not pertain to this Select List scenario.
What is the simplest, most elegant way to resolve this?
I have tried changing the SelectListItem value to a blank string, "-1", but none of it fixes the model validation error.
It can pass the validation if you set it as an empty string.
selectList.Insert(0, new SelectListItem()
{
Value = "",
Text = "--- Select Related Entity ---"
});
You can see the rendered html code, the default option with a value attribute but with no value:
And when submit the form, RootEntity.RelatedEntity_Id will be null.
This is the result.
The best thing I could come up with is the following line of code:
ModelState.Remove("RootEntity.RelatedEntity_Id");
before checking ModelState.IsValid.
This seems to work ok, and is reasonably elegant, I guess.
I am still open to suggestions, and criticisms if I am missing some major language feature, or better practice.
It sounds like Model Validation is behaving as it should. By adding the 'select' option like you did with a null value, model validation will trigger invalid. This is because your model value is not nullable. If you expect the value to be nullable, you should probably adjust your model to accept nulls. Although it does appear to be a table key, so it's not clear why you want this behavior.
public long? Id { get; set; }
Better yet, add this to a ViewModel, along with your selectlist model so that you have a strongly typed list as well.

MVC 5 EnumDropDownListFor implementation

I need to implement a dropdown box for the project. I used enum class to populate it, here is what i have
public class Media
{
public int Id { get; set; }
public string title { get; set; }
public string description { get; set; }
public string tag { get; set; }
public TagsEnum TagsEnum { get; set; }
}
And enum class
public enum TagsEnum
{
Ecomomy,
Latest,
LocalNews,
Interesting,
WorldNews
}
Create method
public ActionResult Create([Bind(Include = "Id,title,description,body,VideoLink, Source,tags")] Media media, HttpPostedFileBase file, int id = 0)
{
if (ModelState.IsValid)
{
if (file != null)
{
file.SaveAs(HttpContext.Server.MapPath("~/Images/")
+ file.FileName);
media.ImagePath = file.FileName;
}
db.Medias.Add(media);
db.SaveChanges();
return RedirectToAction("Index");
}
And here is the problem, on my Create View i have dropdown list working fine, here is the code:
<div class="form-group">
#Html.LabelFor(model => model.tags, "News Category", new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EnumDropDownListFor(model => model.TagsEnum)
#Html.ValidationMessageFor(model => model.tags)
</div>
</div>
But when i open a view page MediaMain were i have a list of media objects, even if i select different categories from dorpdown list i only have 1st one shown. Here is the code
foreach (var b in Model.media)
{
...
<h5>Category: <a href=#Url.Action("Media", new { id = b.Id })>#Html.Raw(b.TagsEnum)</a></h5>
...
}
What do i misunderstand here, please help. Thanks.
The problem is with how you are binding the properties of Media in you controller action result. The Media class has a property public TagsEnum TagsEnum { get; set; } which is bound to the drop down list but in the action result you are not binding it:
[Bind(Include = "Id,title,description,body,VideoLink, Source,tags")] Media media
This causes that the TagsEnum value will not be stored in the database so when you retrieve it in the list of media, it simply returns the first value of the enum. You are using a bunch of other fields like VideoLink or tags which are not properties of Media at all. Those are ignored, but if you want the TagsEnum to be assigned to the media variable, you must add it like this:
[Bind(Include = "Id,title,description,body,VideoLink, Source,tags, TagsEnum")] Media media

MVC 4.6 Binding a drop down from dbContext

I have an mvc empty project where I'm trying to pull questions from the database based on what a user select from a title drop down (The values for the dropdown also come from the database).
I have the drop down working with hard coded values so far. How can I pull the titles from the database and how can I pull the questions associated with the title selected.
The auto generated model I have looks like this
namespace Demo1.Models{
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Web.Mvc;
public partial class Title
{
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")]
public Title()
{
TitlesQuestions = new HashSet<TitlesQuestion>();
}
public int TitleId { get; set; }
[Column("Title")]
[Required]
[StringLength(20)]
public string Title1 { get; set; }
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual ICollection<TitlesQuestion> TitlesQuestions { get; set; }
public SelectList TitleList { get; set; }
}
My ViewModel
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace Demo1.ViewModels.Titles
{
public class TitlesViewModel
{
public int TitleId { get; set; }
[Required]
[Display(Name = "Title")]
public string Title { get; set; }
public IEnumerable<SelectListItem> Titles { get; set; }
}
}
My Controller
using Demo1.Models;
using Demo1.ViewModels.Titles;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
namespace Demo1.Controllers
{
public class TitlesController : Controller
{
private EmployeeContext db = new EmployeeContext();
public IEnumerable<Title> GetTitleList()
{
var result = db.Titles.ToList();
return result;
}
// GET: Titles
public ActionResult Index()
{
var titles = GetAllTitles();
var model = new TitlesViewModel();
model.Titles = GetSelectListItems(titles);
return View(model);
}
private IEnumerable<string> GetAllTitles()
{
return new List<string>
{
"CEO",
"Project Manager",
"Technical Lead",
"Software Developer",
};
}
private IEnumerable<SelectListItem> GetSelectListItems(IEnumerable<string> elements)
{
var selectList = new List<SelectListItem>();
foreach (var element in elements)
{
selectList.Add(new SelectListItem
{
Value = element,
Text = element
});
}
return selectList;
}
}
}
My View
#model Demo1.ViewModels.Titles.TitlesViewModel
#{
ViewBag.Title = "Index";
}
<h2>Questions by Title Demo</h2>
<form asp-controller="Titles" asp-action="Index" method="post" class="form-horizontal" role="form">
<label asp-for="Title" class="col-md-2 control-label"></label>
<div>
#Html.DropDownListFor(m => m.Title, // 1. Store selected value in Model.Roles when page is rendered after postback,take selected value from Model.State.
Model.Titles, // 2. Take list of values from Model.Titles
"- Please select your title -", // 3. Text for the first 'default' option
new { #class = "form-control" }) #*// 4. A class name to assign to <select> tag*#
</div>
</form>
I'm using entity framework
private EmployeeContext db = new EmployeeContext();
I have a table named Titles
Here's my new ActionResult Index()
public ActionResult Index()
{
var model = new TitlesViewModel();
var titles = GetSelectListItems();
model.Titles = titles;
return View(model);
}
public IEnumerable<SelectListItem> GetSelectListItems()
{
foreach (var title in db.Titles)
{
yield return new SelectListItem
{
Value = title.TitleId.ToString(),
Text = title.Title1
};
}
}
When I try to run my project I get the following error
The class 'System.Web.Mvc.SelectList' has no parameterless constructor. I'm new to mvc so I'm not sure how to fix it. This seems like it should be a simple task I'm trying to bind data from the titles table to a drop down then once a title is selected I want the relevant questions from the database to show.
I think this is more of a question that involves on how to query a database. Overall this question needs alittle more context (what database is being used). Depending on what you're using there are many ways to query a database. My favorite for SQL is using ADO.NET Entity Data Model. This is a database first approach. It creates a .edmx that creates all the models from the tables you select when setting it up.
After Setup:
Access data entities
MyAccountEntities users = new MyAccountEntities();
Access tables(use LINQ to change DbSet<UserAccount> to List<UserAccount>) ;)
users.UserAccounts.ToList();
Use LINQ to query your table/list. (returns a list of users named bob)
string username = "bob";
users.UserAccounts.Where(u => u.username == username).ToList();
UPDATE!!!!
Thank you for narrowing your problem down. The problem is pretty straight-forward. You have no constructor that takes parameters for your SelectListItem model. Go to where the model is defined then create a constructor with the parameters you wish to pass it.
Hope this Helps. Cheers :)

How to populate a Select taghelper in Razor using data from a SQL database table

I'm using ASP.NET Core 1.0 and EF Core 1.0 and have the following code-first class in my SQL database.
namespace GigHub.Models
{
public class Genre
{
public byte Id { get; set; }
[Required]
[StringLength(255)]
public string Name { get; set; }
}
}
I also have the following ViewModel class I use in a Razor view form:
namespace GigHub.ViewModels
{
public class GigFormViewModel
{
public string Venue { get; set; }
public string Date { get; set; }
public string Time { get; set; }
public List<Genre> Genres { get; set; }
}
}
I also have this controller:
using GigHub.Data;
using GigHub.ViewModels;
using Microsoft.AspNetCore.Mvc;
namespace GigHub.Controllers
{
public class GigsController : Controller
{
private readonly ApplicationDbContext _context;
public GigsController(ApplicationDbContext context)
{
_context = context;
}
public IActionResult Create()
{
var vm = new GigFormViewModel();
// Need to get my Genre list from the DbSet<Genre> in my database context injected above
// into the GigFormViewModel for the Select taghelper to consume
return View(vm);
}
}
}
I have my Razor view set up to use the ViewModel fine but I'm unsure how the Select taghelper code below should be set up to access the Genre property.
<div class="form-group">
<label asp-for="????" class="col-md-2 control-label"></label>
<div class="col-md-10">
<select asp-for="????" asp-items="????" class="form-control"></select>
<span asp-validation-for="????" class="text-danger" />
</div>
</div>
I'm basically having trouble grokking how to get my list of genres from my database into the ViewModel property in a form that the Select taghelper asp-items= can consume. The many trial & error contortions I've gone through generally result in conversion issues going from generic List<> type to MVCs SelectListItem type. I suspect my ViewModel Genre class needs adjusting but my research thus far has only resulted in articles covering previous versions of ASP.NET and Entity Framework and I struggle to map them to ASP.NET core 1.0 RC2 and EF Core 1.0.
You can use asp-for to specify the model property name for the select element and asp-items to specify the option elements.
<select asp-for="SomeFiles" asp-items="Model.SomeOptions"></select>
You can also use ViewBag.SomeOptions if you don't want to add the SomeOptions filed to the mode.
For more information take a look at The Select Tag Helper documentation.
Example
View
<select asp-for="Country" asp-items="Model.Countries"></select>
Model
using Microsoft.AspNetCore.Mvc.Rendering;
using System.Collections.Generic;
namespace FormsTagHelper.ViewModels
{
public class CountryViewModel
{
public string Country { get; set; }
public List<SelectListItem> Countries { get; set; }
}
}
Controller
The Index method initializes the CountryViewModel, sets the selected country and list of countries and passes the model to the Index view.
public IActionResult Index()
{
var model = new CountryViewModel();
model.Country = "CA";
model.Countries = db.Countries
.Select(x => new SelectListItem { Value = x.Id, Text = x.Name })
.ToList();
return View(model);
}

Dropdownlist with MVC (Asp.net website) [duplicate]

This question already has answers here:
DropDownList in MVC 4 with Razor
(13 answers)
Closed 7 years ago.
I have some trouble creating an dropdownlist correctly in MVC. I'm not sure how to link the dropdownlist with the model, and create values for it. Right now I have the following code that creates 2x dropdownlists:
<div class="form-group">
Outward route<br />
<select class="dropdown" id="Dropdown-outward">
<option>Copenhagen</option>
<option>Oslo</option>
<option>Stockholm</option>
</select>
</div>
<div class="form-group">
Return route<br />
<select class="dropdown" id="Dropdown-return">
<option>Option 1</option>
<option>Option 2</option>
<option>Option 3</option>
</select>
</div>
I used the select & option command to create values as you see in the code. I know you can use some razor syntax like.
#Html. something
but I can't seem to do it right. Have created an model (Booking) which look like this:
namespace Project.Models
{
public class Booking
{
public int ID { get; set; }
public string Departure { get; set; }
public string Return { get; set; }
public DateTime DepartureDate { get; set; }
public DateTime ReturnDate { get; set; }
public int Adults { get; set; }
public int Childrens { get; set; }
}
}
If I use the Html helper like this:
#Html.DropDownList( )
What should i write insite the braces? and how do i add values to it, so you can select, lets say the cities Oslo, Copenhagen & Stockholm?
If you just need the values you can simply use razor, create your model and iterate over a collection of your model like:
<select class="dropdown" id="Dropdown-outward">
#foreach(var m in Model.Items)
{
<option value="#m.ID">#m.ID</option>
}
</select>
This is a possible solution. You have your dropdown and can identify selected values with your model.
If all you want is a simple dropdown list bound to a specific property of your model then you can use the Html.DropdownListFor helper. And it's always a good practice to use a viewmodel with properties that your view needs. So in this case
public class BookingViewModel
{
public Booking Booking { get; set; }
public IEnumerable<SelectListItem> Cities { get; set; } // <-- for your dropdown
}
Controller:
[HttpGet]
public ActionResult Index()
{
var vm = new BookingViewModel();
var citiesList = new List<SelectListItem>
{
new SelectListItem { Value = "Oslo", Text = "Oslo" },
new SelectListItem { Value = "Copenhagen", Text = "Copenhagen" },
new SelectListItem { Value = "Stockholm", Text = "Stockholm" }
};
vm.Cities = citiesList;
return View(vm);
}
View:
#using (Html.BeginForm())
{
#Html.DropDownListFor(s => s.Booking.Departure, Model.Cities)
<button type="submit">Submit</button>
}
Renders a <select id="Booking_Departure" name="Booking.Departure"> ... and <options> that you specified as SelectListItems.
Lastly your post Action:
[HttpPost]
public ActionResult Index(BookingViewModel vm)
// the `ViewModels` -> Booking -> Departure prop will be pupulated with the value from the selection in the dropdown
{
return View(vm);
}

Categories