MVC MultipleModel DropdownlistFor SaveChanges() - asp.net-mvc

HI i Have a MultipleModel View with CompanyName and EmployeeRange. CompanyName contains names of companies with a Relationship link to EmployeeRange (int) Field.
The Employee Range is basically
0-9
10-19
20-49
I can create and SaveChanges for a new Company Name Field.
Please help With Saving Selected value from the EmployeeRange DropDownListFor to DB.
Here is the Code
//MultipleModel.cs
public partial class MultipleModel
{
public MultipleModel()
{
CompanyEntities = new company();
EmployeeEntities = new Employee();
}
public company CompanyEntities { get; set; }
public Employee EmployeeEntities { get; set; }
}
//CompanyController.cs
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult RegisterCompany(MultipleModel model)
{
if (ModelState.IsValid)
{
//GET VARIBALE FROM DB
var addComanyName = db.companies.Add(model.CompanyEntities);
//GET VARIABLLE
addComanyName.COMPANY_NAME = model.CompanyEntities.COMPANY_NAME;
//ADD VARIBALE TO DB
db.companies.Add(addComanyName);
db.SaveChanges();
return RedirectToAction("index");
}
return View();
}
//RegisterCompany.cshtml
#model EISystem.Models.MultipleModel
#Html.DropDownListFor(m => m.CompanyEntities, new SelectList(ViewBag.products, "Employees_Range_ID", "Employees_Range"), "Select Number of Employees")
?? How Do i view the DropDownListFor so that selected Value can be POST to Controller and later be saved to DB?

You should use another class which is ViewModel for your MultipleModel class.
Like:
public class MultipleModelViewModel
{
public int SelectedProductId { get; set; }
public List<Products> ProductList{ get; set; }
public string Name { get; set; }
}
In a View
#model MultipleModelViewModel
#using (Html.BeginForm("RegisterCompany", "Company", FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="form-group">
#Html.LabelFor(model => model.SelectedProductId , htmlAttributes: new { #class = "form-control" })
#Html.DropDownListFor(model => model.SelectedProductId, new SelectList(Model.ProductList, "Id", "Name"), "Select Product", new { #class = "form-control " })
</div>
<div class="form-group">
#Html.LabelFor(model => model.Name, new { #class = "form-control " })
#Html.TextBoxFor(model => model.Name, new { #class = "form-control" })
</div>
<button type="Submit" class="btn btn-success " id="Save-btn">
Save
</button>
}
Controller:
//Get
public ActionResult RegisterCompany()
{
var model = new MultipleModelViewModel (){
Name = model.Name,
Products = db.Products.Select(x=>new {Id=x.Id, Name=x.Name).ToList()
};
return View(model);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult RegisterCompany(MultipleModelViewModel model)
{
if (ModelState.IsValid)
{
var company = new Company(){
Name = model.Name,
Product = db.Products.Find(model.SelectedProductId)
};
db.companies.Add(company);
db.SaveChanges();
return RedirectToAction("index");
}
return View();
}

Related

How to store and display SelectList data

I am using SelectList to populate dropdownlists in my view. It works for the Create and Edit views to store ID value data in a table. How do I retrieve the 'Name' value to display in a Details view?
Model
Public Class Employee {
[Key]
public int ID { get; set;}
public string UserName {get; set; }
public byte Gender { get; set; }
}
ViewModel
public class EmployeeEditViewModel {
public int ID { get; set; }
public string UserName { get; set; }
public SelectList GenderList { get; set; }
public EmployeeEditViewModel () {
GenderList = CommonHelper.GenderList(null);
}
}
Helper
public static SelectList GenderList(object selected)
{
return new SelectList(new[]
{
new { Value = 0, Name = "Male" },
new { Value = 1, Name = "Female" }
}
, "Value", "Name", selected);
}
Edit View
#model Models.ViewModel.EmployeeEditViewModel
#using (Html.BeginForm()) {
#Html.HiddenFor(model => model.ID)
<div class="form-group">
#Html.LabelFor(model => model.UserName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.UserName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.UserName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.GenderList, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.Gender, Model.GenderList, "- Select -", new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.GenderList, "", new { #class = "text-danger" })
</div>
</div>
}
Controller
[HttpPost]
public ActionResult CreateEmployee(EmployeeEditViewModel emProfile)
{
try
{
if (ModelState.IsValid)
{
Employee newUser = new Employee();
newUser.UserName = emProfile.UserName;
newUser.Gender = emProfile.Gender;
userRepository.Add(newUser);
userRepository.SaveChanges();
return RedirectToAction("Index");
}
}
catch (Exception ex)
{ }
return View(emProfile);
}
So far it works great, I am able to create, edit Employee records and 1 or 0 is stored in the table for the gender.
But when I want to display the employee data in a details view how do I get the text 'Male' or 'Female'?
I ended up creating a helper method to retrieve the text.
public static string GetTextFromSelectList(int id, string listName)
{
string selectedText = string.Empty;
SelectListItem selectedItem;
switch (listName)
{
case "Gender":
selectedItem = Helper.CommonHelper.GenderList(null).FirstOrDefault(x => x.Value == id.ToString());
selectedText = selectedItem == null ? null : selectedItem.Text;
break;
default:
selectedText = null;
break;
}
return selectedText;
}

how i can send multivalue to create action

i have a doctor i want add doctor subspecialty to the doctor from sub specialties table many to many relationship
i need to add subspecialties from multiselect list but my controller only add first selection , i want my create controller take all passed subspecialties and create it
my model
public partial class DoctorSubSpecialty
{
public int Id { get; set; }
public Nullable<int> DoctorId { get; set; }
public Nullable<int> SubSpecialtyId { get; set; }
public virtual DoctorProfile DoctorProfile { get; set; }
public virtual SubSpecialty SubSpecialty { get; set; }
}
}
create get action
public ActionResult Create()
{
ViewBag.DoctorId = new SelectList(db.DoctorProfiles, "Id", "FullName");
ViewBag.SubSpecialtyId = new MultiSelectList(db.SubSpecialties, "id", "Name");
return View();
}
create post action
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create([Bind(Include = "Id,DoctorId,SubSpecialtyId")] DoctorSubSpecialty doctorSubSpecialty)
{
DoctorSubSpecialty doctorSub = db.DoctorSubSpecialties.Where(d => d.DoctorId == doctorSubSpecialty.DoctorId & d.SubSpecialtyId == doctorSubSpecialty.SubSpecialtyId).FirstOrDefault();
if (doctorSub == null) {
db.DoctorSubSpecialties.Add(doctorSubSpecialty);
await db.SaveChangesAsync();
}
my view
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>DoctorSubSpecialty</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.DoctorId, "DoctorId", htmlAttributes: new { #class = "control-label col-md-2", #id = "DoctorID" })
<div class="col-md-10">
#Html.DropDownList("DoctorId", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.DoctorId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.SubSpecialtyId, "SubSpecialtyId", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("SubSpecialtyId",(MultiSelectList)ViewBag.SubSpecialtyId, htmlAttributes: new { #multiple = "multiple", #class = "form-control" })
#Html.ValidationMessageFor(model => model.SubSpecialtyId, "", new { #class = "text-danger" })
</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>
}
Create a ViewModel specific to your usecase that can actually transport more than one Id.
I.e. you will need an int[] to bind the selection to.
A ViewModel also helps you to get rid of all this ViewBag and [Bind] nonsense.
public class CreateDoctorSubSpecialtyViewModel {
// These are the selected values to be posted back
public int DoctorId { get; set; }
public int[] SubSpecialtyIds { get; set; }
// These are the possible values for the dropdowns
public IEnumerable<SelectListItem> DoctorProfiles { get; set; }
public IEnumerable<SelectListItem> SubSpecialties { get; set; }
}
GET action - initialize the ViewModel and pass it to the View:
[HttpGet]
public ActionResult Create() {
var doctorProfiles = db.DoctorProfiles.Select(d =>
new SelectListItem {
Text = d.FullName,
Value = d.Id
}
).ToArray();
var subSpecialties = db.SubSpecialties.Select(s =>
new SelectListItem {
Text = s.Name,
Value = s.id
}
).ToArray();
var viewModel = new CreateDoctorSubSpecialtyViewModel {
DoctorProfiles = doctorProfiles,
SubSpecialties = subSpecialties
};
return View("Create", viewModel);
}
View "Create.cshtml" (styling removed for clarity) - tell MVC which ViewModel we want to use with #model:
#model CreateDoctorSubSpecialtyViewModel
#using (Html.BeginForm("Create", "YourControllerName", FormMethod.Post)) {
#Html.DropDownListFor(m => m.DoctorId, Model.DoctorProfiles)
#Html.DropDownListFor(m => m.SubSpecialtyIds, Model.SubSpecialties, new { multiple = "multiple" })
<input type="submit" />
}
POST action - use Linq Contains to test against multiple submitted SubSpecialtyIds:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create(CreateDoctorSubSpecialtyViewModel postData) {
DoctorSubSpecialty[] allSelectedSubSpecialities = db.DoctorSubSpecialties
.Where(d => d.DoctorId == postData.DoctorId
&& postData.SubSpecialtyIds.Contains(d.SubSpecialtyId))
.ToArray();
// ...
}
EDIT #Html.DropDownListFor requires an IEnumerable<SelectListItem> as second parameter.

The ViewData item that has the key 'Position' is of type 'System.String' but must be of type 'IEnumerable<SelectListItem>' [duplicate]

This question already has answers here:
The ViewData item that has the key 'XXX' is of type 'System.Int32' but must be of type 'IEnumerable<SelectListItem>'
(6 answers)
Closed 6 years ago.
I am populating DropDownList from in-memory data and getting this error on POST.
The ViewData item that has the key 'Position' is of type 'System.String' but must be of type 'IEnumerable'.
Model:
public class StaffModel
{
public int id { get; set; }
public string Email { get; set; }
[DataType(DataType.Password)]
public string Password { get; set; }
[DataType(DataType.Password)]
public string PasswordConfirm { get; set; }
public string Emp_Name { get; set; }
public string Emp_Address { get; set; }
public string Phone { get; set; }
public string Position { get; set; }
public List<SelectListItem> Positions { set; get; }
}
Controller:
public ActionResult Register()
{
IEnumerable<SelectListItem> position = db.Positions.Select(p => new SelectListItem
{
Text = p.Position_Title,
Value = p.Position_ID.ToString()
});
ViewBag.Position = position;
return View();
}
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Register(StaffModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = await UserManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
Employee em = new Employee
{
Employee_Name = model.Emp_Name,
Address = model.Emp_Address,
Phone = model.Phone,
Position_ID = Convert.ToInt32(db.Positions.Where(p => p.Position_Title == model.Position).Select(p => p.Position_ID)),
};
db.Employees.Add(em);
db.SaveChanges();
return RedirectToAction("Index", "Employees");
}
}
return View(model);
}
enter code here
HTML/Razor:
<div class="form-group">
#Html.LabelFor(model => model.Position, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("Position",null, htmlAttributes: new { #class = "form-control" } )
#Html.ValidationMessageFor(model => model.Position, "", new { #class = "text-danger" })
</div>
</div>
Your binding on the drop down list doesn't look right.
You're model contains Positions which is the data for your drop down. You also have "Position" which I presume is a string that will be bound to the selected value in the drop down.
Change your view to
#Html.DropDownListFor(x=> x.Position, Model.Positions, new {#class = "form-control"}) )
Then on your post, it looks as if you are trying to get the value selected from the drop down by performing a linq query on the "Positions" list of the model which was used to populate your dropdown. You should now have the selected value in the "Position" property of your model.
So you should say
Position_ID = model.Position
I'd also use a model for your Register View.
Something like...
public class RegisterViewModel
{
public IEnumerable<SelectListItem> Positions { get; set; }
public string Position { get; set; }
}
Plus the additional fields you need.
The in your Register action method, populate the view model and return the view.
public ActionResult Register()
{
var regModel = new RegisterViewModel
{
Positions = db.Positions.Select(p => new SelectListItem
{
Text = p.Position_Title,
Value = p.Position_ID.ToString()
})
};
return View("Register",regModel);
}
You don't need to use ViewBag now.
Hope that helps
it work correctly. thank everybody help me:D
Model:
public class StaffModel
{
public string Position { get; set; }
public List<Position> Positions { set; get; }
public int selectID { get; set; }
}
Controller:
public ActionResult Register()
{
StaffModel st = new StaffModel();
st.Positions = db.Positions.ToList();
return View(st);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Register(StaffModel model)
{
if (ModelState.IsValid)
{
var user = new ApplicationUser { UserName = model.Email, Email = model.Email };
var result = UserManager.Create(user, model.Password);
if (result.Succeeded)
{
Employee em = new Employee
{
Employee_Name = model.Emp_Name,
Address = model.Emp_Address,
Phone = model.Phone,
Position_ID = model.selectID,
ID_User = user.Id
};
db.Employees.Add(em);
db.SaveChanges();
return RedirectToAction("Index", "Employees");
}
else
AddErrors(result);
}
ViewBag.Position = new SelectList(db.Positions, "Position_ID", "Position_Title");
return View(model);
}
HTML/Razor:
<div class="form-group">
#Html.LabelFor(model => model.Position, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model=>model.selectID,new SelectList(Model.Positions, "Position_ID", "Position_Title"), htmlAttributes: new { #class = "form-control" } )
#Html.ValidationMessageFor(model => model.Position, "", new { #class = "text-danger" })
</div>
</div>

MVC model conflict using partial class and Validate

I have a page containing a form and a partial view (containing a form too).
both model have 1 (or more) properties with the same name. when I validate the first form, the value and validation message is duplicate on the second form.
I create a little sample with dummy entities.
person.cs
public partial class Person : IValidatableObject
{
[Required(ErrorMessage = "name required")]
public string Name { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var results = new List<ValidationResult>();
if (Name == "admin") //just example
{
results.Add(new ValidationResult("You cant be admin.", new[] { "Title", "Name" }));
}
return results;
}
}
Person/Index.cshtml
#model Person
#{
ViewBag.Title = "Person";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#using (Html.BeginForm("Index", "Person", FormMethod.Post, new { id = "CreatePersonForm" }))
{
#Html.AntiForgeryToken()
#Html.DisplayNameFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
<input type="submit" value="Save" class="btn btn-default" />
}
#Html.Partial("~/Views/Dog/Index.cshtml", new Dog())
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
PersonController.cs
public class PersonController : Controller
{
// GET: Person
public ActionResult Index()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index([Bind(Include = "Name")] Person person)
{
if (ModelState.IsValid)
{
return RedirectToAction("Index");
}
return View(person);
}
}
I made a partial view practically the same.
Dog.cs
public partial class Dog : IValidatableObject
{
[Required(ErrorMessage = "name required")]
public string Name { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var results = new List<ValidationResult>();
if (Name == "admin") //just example
{
results.Add(new ValidationResult("You cant be admin.", new[] { "Title", "Name" }));
}
return results;
}
}
Dog/Index.cshtml
#model Dog
#{
ViewBag.Title = "Dog Page";
}
#using (Html.BeginForm("Index", "Dog", FormMethod.Post, new { id = "CreateDogForm" }))
{
#Html.AntiForgeryToken()
#Html.DisplayNameFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger" })
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control" } })
<input type="submit" value="Save" class="btn btn-default" />
}
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
DogController.cs
public class DogController : Controller
{
// GET: Dog
public ActionResult Index()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Index([Bind(Include = "Name")] Dog dog)
{
if (ModelState.IsValid)
{
return RedirectToAction("Index");
}
return View(dog);
}
}
if you start /Person/Index, if you write admin in the first textbox (person form), after posting (save) the second form (dog form) have the same text and validation than the first form.
The #Html.EditorFor by default uses the property name as the id and name of the generated HTML, and the validation uses these values to set the error messages! You can pass a value to overwrite that default behavior in your partial view as following:
#Html.ValidationMessageFor(model => model.Name, "", new { #class = "text-danger", #data_valmsg_for="partial_name" })
#Html.EditorFor(model => model.Name, new { htmlAttributes = new { #class = "form-control", #id="partial_name" } })

How to save dropdownlist selected value to the database int asp.net MVC 5

I am currently new to Asp.net MVC .In one of the view I add a dropdownlist and I bind this dropdownlist with my database like this
Controller CollegeController
[HttpGet]
public ActionResult Create()
{
IEnumerable<SelectListItem> items = db.College_Names.Select(c => new SelectListItem { Value = c.id.ToString(), Text = c.Name });
IEnumerable<SelectListItem> item = db.Stream_Names.Select(c => new SelectListItem { Value = c.id.ToString(), Text = c.Stream });
ViewBag.CollName=items;
ViewBag.StreamName = item;
return View();
}
[HttpPost]
public ActionResult Create(College college)
{
try
{
if(ModelState.IsValid)
{
db.Colleges.Add(college);
db.SaveChanges();
return RedirectToAction("Index");
}
ViewBag.CollName = db.Colleges;
return View(college);
}
catch
{
return View();
}
}
This is my model
public class College
{
[Required]
public int Id { get; set; }
[Required]
[Display(Name="College Name")]
public int CollegeName { get; set; }
[Required]
public int Stream { get; set; }
[Required]
[Column(TypeName="varchar")]
public string Name { get; set; }
....
public virtual College_Name College_Name { get; set; }
public virtual Stream_Name Stream_Name { get; set; }
}
This is My View
<div class="form-group">
#Html.LabelFor(model => model.CollegeName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("CollName", (IEnumerable<SelectListItem>)ViewBag.CollName, "Select College", new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.CollegeName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Stream, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("StreamName", (IEnumerable<SelectListItem>)ViewBag.StreamName, "Select Stream", new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.Stream, "", new { #class = "text-danger" })
</div>
</div>
Now when I check my database after I save the CollegeName and Stream in the database is zero from the dropdownlist.
You have multiple problems with your code. Firstly you dropdownlists are binding to a properties named CollName and StreamName which do not even exist in your model.
Next you cannot name the property your binding to the same as the ViewBag property.
Your view code would need to be (and always use the strongly typed xxxFor() HtmHelper methods
#Html.DropDownListFor(m => m.CollegeName, (IEnumerable<SelectListItem>)ViewBag.CollName, "Select College", new { #class = "form-control" })
....
#Html.DropDownListFor(m => m.Stream, (IEnumerable<SelectListItem>)ViewBag.StreamName, "Select Stream", new { #class = "form-control" }
and in your POST method, the values of college.CollegeName and college.Stream will contain the ID's of the selected options.
You also need to repopulate the ViewBag properties when you return the view in the POST method (as you did in the GET method) or an exception will be thrown (and note that your current use of ViewBag.CollName = db.Colleges; will also throw an exception)
I also strongly suggest you start learning to use view models (views for editing should not use data models - refer What is ViewModel in MVC?) - and use naming conventions that reflect what your properties are, for example CollegeNameList, or CollegeNames, not CollName

Resources