Create View with Model and List<Model> - asp.net-mvc

I have a project model and a technology model. There can be multiple technologies in one project. There will be a limited amount of technology objects, around 5-10, that will be used across an unlimited amount of projects. I thought of keeping the technology items in an enum or just as constant variables, but I want the ability to add or edit down the road, so I figured I would put them in a table.
I tend to get hung up when dealing with lists. I want to be able to create a single project, and in the same view check a checkbox for the technology that was used on the project (ex. HTML5 or CSS3). I am not able to get a list of technologies to show up in the view for me to mark as checked for the particular project.
Project Class:
public class Project
{
public int Id { get; set; }
public string Website { get; set; }
public List<Technology> Technologies { get; set; }
}
Technology Class:
public class Technology
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsActive { get; set; }
}
ViewModel:
public class ProjectTechnologyViewModel
{
public IEnumerable<Technology> Technologies { get; set; }
public Project Projects { get; set; }
public ProjectTechnologyViewModel()
{
Projects = new Project();
}
}
DbContext:
public class NovaDB : DbContext
{
public NovaDB(): base("DefaultConnection")
{
}
public DbSet<Technology> Technologies { get; set; }
public DbSet<Project> Projects{ get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
Controller:
public ActionResult CreateProject()
{
return View();
}
[HttpPost]
public ActionResult CreateProject(ProjectTechnologyViewModel viewModel)
{
var proj = new Project();
if (ModelState.IsValid)
{
proj.LaunchDate = viewModel.Projects.LaunchDate;
proj.ProjectType = viewModel.Projects.ProjectType;
proj.ServiceId = viewModel.Projects.ServiceId;
proj.ShowInOurWork = viewModel.Projects.ShowInOurWork;
proj.Website = proj.Website;
proj.Technologies = new List<Technology>();
foreach (var t in viewModel.Technologies)
{
proj.Technologies.Add(new Technology()
{
Name = t.Name,
FAClassName = t.FAClassName,
IsActive = t.IsActive,
});
}
_db.Projects.AddOrUpdate(proj);
_db.SaveChanges();
}
return View(proj);
}
View:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Project</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Projects.Website, htmlAttributes: new {#class = "control-label col-md-2"})
<div class="col-md-10">
#Html.EditorFor(model => model.Projects.Website, new {htmlAttributes = new {#class = "form-control"}})
#Html.ValidationMessageFor(model => model.Projects.Website, "", new {#class = "text-danger"})
</div>
</div>
#for (int i = 0; i < Model.Technologies.Count(); i++)
{
#Html.CheckBoxFor(x=>x.Technologies[i].)
}
<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>
}
Image of View:

The checkbox is for the IsActive property of the Technology, right? So, you can do this:
#Html.CheckBoxFor(model => model.Technologies.ToList()[i].IsActive) #Model.Technologies.ToList()[i].Name
You also need to instantiate your ViewModel and send it to your view in your GET action, like this:
public ActionResult CreateProject()
{
var model = new ProjectTechnologyViewModel();
model.Project = new Project();
model.Technologies = new List<Technology>();
return View(model);
}
Also, don't instantiate the properties in your ViewModel. Do that in your controller (see above):
public class ProjectTechnologyViewModel
{
public IEnumerable<Technology> Technologies { get; set; }
public Project Project { get; set; }
}

Why are you referring to a IEnumerable as an array?
this - someIEnumerable[0] will not be compiled.
Why don't you use foreach?
foreach(var i in Model.Technologies){
var a = i.Website;
}

Firstly, you will need a table to store the Technologies associated with a Project, (say ProjectTechnlogies) that will include FK fields to the Project ID field and the Technology ID field.
Then you will need view models to represent what your wanting to edit in the view
public class TechnologyVM
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsSelected { get; set; } // for binding to a checkbox
}
public class ProjectVM
{
public string Website { get; set; }
public IEnumerable<TechnologyVM> Technologies { get; set; }
}
and your GET method would be
public ActionResult Create()
{
ProjectVM model = new ProjectVM
{
Technologies = _db.Technologies.Where(t => t.IsActive).Select(t => new TechnologyVM
{
Id = t.Id,
Name = t.Name
})
};
return View(model);
}
Note I am assuming here that your IsActive property means that if the value is false its now an obsolete technology (it may have been assigned to previous Projects) and that you don't want to allow to to be selected in the view when creating a new Project.
Then create an EditorTemplate (partial) for TechnologyVM in /Views/Shared/EditorTemplates/TechnologyVM.cshtml (note the name of the file must match the name of the class)
#model TechnologyVM
<div>
#Html.HiddenFor(m => m.Id)
#Html.HiddenFor(m => m.Name) // if you want this value to be posted
#Html.CheckBoxFor(m => m.IsSelected)
#Html.LabelFor(m => m.IsSelected, Model.Name)
</div>
and the main view will be
#model ProjectVM
....
#using (Html.BeginForm())
{
....
#Html.TextBoxFor(m => m.Website)
....
#Html.EditorFor(m => m.Technologies)
<input type="submit" ... />
}
The EditorFor() method will correctly generate the html for each Technology
Finally you POST method will be
[HttpPost]
public ActionResult Create(ProjectVM model)
{
if (!ModelState.IsValid)
{
return View(model);
}
Project project = new Project
{
Website = model.Website,
// ... other properties of Project
}
_db.Projects.Add(project);
_db.SaveChanges(); // The Project's Id property has now been set
IEnumerable<int> selectedTechnologies = model.Where(t => t.IsSelected).Select(t => t.Id);
foreach(int ID in selectedTechnologies)
{
ProjectTechnology technology = new ProjectTechnology
{
ProjectId = project.Id,
Technology = ID
};
_db.ProjectTechnologies.Add(technology);
}
_db.SaveChanges(); // save the project technologies
// redirect
}

I had to change a few models and I added a table to map the two models. I decided to save the "link" between the Project & Technology models in a shared table called ProjectTechnologyMap. For the project I create, I insert a record with the project Id and the Technology Id. The project Id stays constant and there are multiple technology Ids, depending on how many are selected in the view. I also save my project details to the project table. I do not save anything to the technology table as it is static (I do not create technologies on my Create Project View).
Here is my solution that worked:
Models:
public class Project
{
public int Id { get; set; }
public string Website { get; set; }
}
public class Technology
{
public int Id { get; set; }
public string Name { get; set; }
public bool IsActive { get; set; }
}
public class ProjectTechnologyMap
{
public int Id { get; set; }
public int ProjectId { get; set; }
public int TechnologyId { get; set; }
}
ViewModel:
public class ProjectTechnologyViewModel
{
public List<Technology> Technologies { get; set; }
public Project Projects { get; set; }
public ProjectTechnologyMap ProjectTechnologyMapping { get; set; }
public ProjectTechnologyViewModel()
{
Projects = new Project();
ProjectTechnologyMapping = new ProjectTechnologyMap();
}
}
DB Context:
public class NovaDB : DbContext
{
public NovaDB(): base("DefaultConnection")
{
}
public DbSet<Technology> Technologies { get; set; }
public DbSet<Project> Projects { get; set; }
public DbSet<ProjectTechnologyMap> ProjectTechnologyMappings { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
}
}
Controller:
public ActionResult Create()
{
var technologies = from t in _db.Technologies
orderby t.Name
where t.IsActive
select t;
var viewModel = new ProjectTechnologyViewModel();
viewModel.Technologies = technologies.ToList();
return View(viewModel);
}
[HttpPost]
public ActionResult Create(ProjectTechnologyViewModel viewModel)
{
if (ModelState.IsValid)
{
var proj = new Project
{
LaunchDate = viewModel.Projects.LaunchDate,
ProjectType = viewModel.Projects.ProjectType,
ServiceId = viewModel.Projects.ServiceId,
ShowInOurWork = viewModel.Projects.ShowInOurWork,
Website = viewModel.Projects.Website
};
_db.Projects.AddOrUpdate(proj);
_db.SaveChanges();
var map = new ProjectTechnologyMap();
foreach (var item in viewModel.Technologies)
{
if (item.UsedInProject)
{
map.TechnologyId = item.Id;
map.ProjectId = proj.Id;
_db.ProjectTechnologyMappings.Add(map);
_db.SaveChanges();
}
}
}
return RedirectToAction("Index", "Project");
}
Loop in my View to handle the list of technologies:
#for (var i = 0; i < Model.Technologies.Count; i++)
{
<div class="form-group">
#Html.LabelFor(x => x.Technologies[i].Name, Model.Technologies[i].Name, new {#class = "control-label col-md-2"})
<div class="col-md-10">
<div class="checkbox">
#Html.EditorFor(x => x.Technologies[i].UsedInProject)
#Html.HiddenFor(x => x.Technologies[i].Id)
</div>
</div>
</div>
}

Related

Problem in showing ViewModel in Create form

I am learning how to use ViewModel to show the fields from 2 different models. I have one model containing the MsgTypeId, MsgType and MsgStatus and the another model OptStatus containing the StatusId, StatusName and StatusValue. The MsgStatus will be shown in form of drop down list and show all the values in OptStatus. Both models have a separate database table to store their values.
namespace theManager.Areas.Settings.Models
{
public class OptStatus
{
[Required]
[Key]
public int StatusId { get; set; }
[Required]
public string StatusName { get; set; }
[Required]
public char StatusValue { get; set; }
}
}
namespace theManager.Areas.Settings.Models
{
public class OptMsgType
{
[Required]
[Key]
public int MsgTypeId { get; set; }
[Required]
public string MsgType { get; set; }
[Required]
public string MsgStatus { get; set; }
}
}
I have created a ViewModel to show these fields in the Create form of OptMsgType. However, when I run the code, I got an error
"System.NullReferenceException: 'Object reference not set to an instance of an object.'"
I would like to ask if there is something wrong with my ViewModel. Thanks!
namespace theManager.Areas.Settings.ViewModels
{
public class OptMsgTypeCreateViewModel
{
public OptMsgType OptMsgType { get; set; }
public IEnumerable<SelectListItem> OptStatuses { get; set; }
}
}
OptMsgTypeController.cs
public IActionResult Create(int id)
{
var OptMsgTypeViewModel = new OptMsgTypeCreateViewModel();
OptMsgTypeViewModel.OptStatuses = _context.OptStatus.ToList().Select(x => new SelectListItem
{
Text = x.StatusName,
Value = x.StatusValue.ToString()
});
OptMsgTypeViewModel.OptMsgType = _context.OptMsgType.Where(a => a.MsgTypeId == id).FirstOrDefault();
//var v = _context.OptMsgType.Where(a => a.MsgTypeId == id).FirstOrDefault();
return View(OptMsgTypeViewModel);
}
I have problems in displaying the Create form which will show the fields declared in the ViewModel.
#model theManager.Areas.Settings.ViewModels.OptMsgTypeCreateViewModel
#{
ViewData["Title"] = "Create";
Layout = null;
}
<h2>Message Type Settings</h2>
#using (Html.BeginForm("Create","OptMsgType", FormMethod.Post, new { id= "popupForm" }))
{
if (Model != null && Model.OptMsgType.MsgTypeId > 0)
{
#Html.HiddenFor(a=>a.OptMsgType.MsgTypeId)
}
<div class="form-group">
<label>Message Type ID</label>
#Html.TextBoxFor(a=>a.OptMsgType.MsgTypeId,new { #class = "form-control" })
#Html.ValidationMessageFor(a=>a.OptMsgType.MsgTypeId)
</div>
<div class="form-group">
<label>Leave Type</label>
#Html.TextBoxFor(a => a.OptMsgType.MsgType, new { #class = "form-control" })
#Html.ValidationMessageFor(a => a.OptMsgType.MsgType)
</div>
<div class="form-group">
<label>Status</label>
#Html.DropDownListFor(model => model.OptStatuses, new SelectList(Model.OptStatuses, "Value", "Text"), htmlAttributes: new { #class = "form-control", id = "OptStatus" })
#Html.ValidationMessageFor(a => a.OptStatuses)
</div>
<div>
<input type="submit" value="Create" />
</div>
}
The System.NullReferenceException indicates that you are using a field without initializing it. It coulbe a problem with your view model or it could be a problem anywere else. For example from the code smaple is not possible to see where you initialize the context you are using to get the data, and that could be the cause of the exception you are getting.
Either way I would advise you to pay attention to yout IDE, it usualy indicates in which line adnd class the exception is being thown. If you navigate to that class at that line you will easily identify which field can be de cause of the exception.
Regarding your view model, its considered a good practice to always initialize the lists on your model on the constructor of your class. This way you can guarantee that they are already initialized when you try to use them.
So my sugestion would be to initialize your list on the constructor of your viewmodel
public OptMsgTypeCreateViewModel()
{
OptStatuses = new List<OptStatus>();
}
#George, thanks for the reply. Please try this then: instantiate your class in the viewmodel.
public class OptMsgTypeCreateViewModel
{
public OptMsgTypeCreateViewModel()
{
OptMsgType = new OptMsgType();
}
public OptMsgType OptMsgType { get; set; }
public IEnumerable<SelectListItem> OptStatuses { get; set; }
}
hi in action controller you should change this code:
OptMsgTypeViewModel.OptStatuses = _context.OptStatus.ToList().Select(x => new SelectListItem
{
Text = x.StatusName,
Value = x.StatusValue.ToString()
});
I think _context.OptStatus.ToList() in null so you get this exception. change code to this:
OptMsgTypeViewModel.OptStatuses =new list<SelectListItem>();
var temp= _context.OptStatus.ToList();
if(temp!=null&&temp.count()>0)
{
OptMsgTypeViewModel.OptStatuses = temp.Select(x => new SelectListItem
{
Text = x.StatusName,
Value = x.StatusValue.ToString()
}).tolist();
}
EDIT:
I think this object "Model.OptMsgType" is null
change code in view like this:
if (Model != null && Model.OptMsgType!=null && Model.OptMsgType.MsgTypeId > 0)
{
#Html.HiddenFor(a=>a.OptMsgType.MsgTypeId)
}

Asp.Net MVC dropdownlist not posting value to controller -> database

my development environment is ASP.NET MVC 5 using Entity Framework 6 using a code-first workflow.
My problem: When trying to add a new record to the database, the values from my two dropdownlists are not being included. All the other fields are indeed saving to the database.
I am passing a ViewModel to the view in question:
public class NewItemViewModel
{
public IEnumerable<Category> Categories { get; set; }
public Item Item { get; set; }
public IEnumerable<Donor> Donors { get; set; }
}
The domain models I am using:
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public int Quantity { get; set; }
public bool IsActive { get; set; }
public Category Category { get; set; }
public Donor Donor { get; set; }
}
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
}
public class Donor
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string FullName { get { return FirstName + " " + LastName; } }
public string Email { get; set; }
}
The relevant controller: This New() action is simply used to pass data to the view for the form to be submitted
public ActionResult New()
{
var itemCategories = _context.ItemCategory.ToList();
var donors = _context.Donors.ToList();
var viewModel = new NewItemViewModel
{
Categories = itemCategories,
Donors = donors
};
return View(viewModel);
}
Here is my actual action that adds the submitted data to the database:
// Create a new item.
[HttpPost]
public ActionResult Create(Item item)
{
_context.Items.Add(item);
_context.SaveChanges();
return RedirectToAction("Index", "Item");
}
And lastly, the view itself (I will minimize the code here):
#using (Html.BeginForm("Create", "Item"))
{
<div class="form-group">
#Html.LabelFor(m => m.Item.Donor)
#Html.DropDownListFor(m => m.Item.Donor, new SelectList(Model.Donors, "Id", "FullName"), "Who donated this item?", new { #class = "form-control" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.Item.Category)
#Html.DropDownListFor(m => m.Item.Category, new SelectList(Model.Categories, "Id", "Name"), "Select Item Category", new { #class = "form-control" })
</div>
}
To reiterate the problem I am having: The values of the dropdownlist for Category and Donor are not saving to the database, whereas the others (non-navigation properties?) Name, Description, Quantity, etc. is working correctly.
Am I using the ViewModel the right way? I was under the impression that the MVC framework knows how to deal with passing the Item object parameter in the Create() action - mapping what it needs to within the Item entity.
Any help would be much appreciated. Thank you.
In the NewItemViewModel you have not created property to hold the selected values from dropdown
public class NewItemViewModel
{
public IEnumerable<Category> Categories { get; set; }
public int SelectedCategory{get;set;}
public Item Item { get; set; }
public int SelectedDonor{get;set;}
public IEnumerable<Donor> Donors { get; set; }
}
#using (Html.BeginForm("Create", "Item"))
{
<div class="form-group">
#Html.LabelFor(m => m.Item.Donor)
#Html.DropDownListFor(m => m.SelectedDonor, new SelectList(Model.Donors, "Id", "FullName"), "Who donated this item?", new { #class = "form-control" })
</div>
<div class="form-group">
#Html.LabelFor(m => m.Item.Category)
#Html.DropDownListFor(m => m.SelectedCategory, new SelectList(Model.Categories, "Id", "Name"), "Select Item Category", new { #class = "form-control" })
</div>
}

Unable to populate checkbox from database data in mvc 4

This is my Controller code.
public ActionResult Create()
{
ViewBag.grp_id = new SelectList(db.tm_grp_group, "grp_id", "grp_name");
ViewBag.perm_id = new SelectList(db.tm_perm_level, "perm_id", "perm_levelname");
return View();
}
Below is my view code.
#model Permission.ts_grp_perm_mapping
....
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>ts_grp_perm_mapping</legend>
<div class="editor-label">
#Html.LabelFor(model => model.grp_permid)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.grp_permid)
#Html.ValidationMessageFor(model => model.grp_permid)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.grp_id, "tm_grp_group")
</div>
<div class="editor-field">
#Html.DropDownList("grp_id", String.Empty)
#Html.ValidationMessageFor(model => model.grp_id)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.perm_id, "tm_perm_level")
</div>
<div class="editor-field">
#Html.DropDownList("perm_id", String.Empty)
#Html.ValidationMessageFor(model => model.perm_id)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
In controller ViewBag.perm_id contains some values (this is foreign key). In view perm.id displays in the form of dropdownbox but I want it in the form of checkboxlist. How can I achieve this?
This is the viewmodel I created.
public class AssignUserViewModel
{
public tm_perm_level[] perms { get; set; }
public int grp_id { get; set; }
}
Now in controller how can i send values to view? This is my tm_perm_level model
public partial class tm_perm_level
{
public tm_perm_level()
{
this.ts_grp_perm_mapping = new HashSet<ts_grp_perm_mapping>();
}
public int perm_id { get; set; }
public string perm_levelname { get; set; }
public string perm_description { get; set; }
public bool perm_status { get; set; }
public virtual ICollection<ts_grp_perm_mapping> ts_grp_perm_mapping { get; set; }
}
This is ts_grp_perm_mapping model
public partial class ts_grp_perm_mapping
{
public ts_grp_perm_mapping()
{
this.ts_perm_levelmapping = new HashSet<ts_perm_levelmapping>();
}
public int grp_permid { get; set; }
public int grp_id { get; set; }
public int perm_id { get; set; }
public List<tm_perm_level> permissions { get; set; }
public virtual tm_grp_group tm_grp_group { get; set; }
public virtual tm_perm_level tm_perm_level { get; set; }
public virtual ICollection<ts_perm_levelmapping> ts_perm_levelmapping { get; set; }
}
Especially when editing, always start with view models to represent what you want to display (refer What is ViewModel in MVC?)
public class PermissionVM
{
public int ID { get; set; }
public string Name { get; set; }
public bool IsSelected { get; set; }
}
public class GroupPermissionVM
{
public int GroupID { get; set; }
public IEnumerable<SelectListItem> GroupList { get; set; }
public IEnumerable<PermissionVM> Permissions { get; set; }
}
Then create an EditorTemplate for PermissionVM. In the /Views/Shared/EditorTemplates/PermissionVM.cshtml folder
#model PermissionVM
<div>
#Html.HiddenFor(m => m.ID)
#Html.HiddenFor(m => m.Name)
#Html.CheckBoxFor(m => m.IsSelected)
#Html.LabelFor(m => m.IsSelected, Model.Name)
</div>
and the main view will be
#model GroupPermissionVM
....
#using (Html.BeginForm())
{
// dropdownlist
#Html.LabelFor(m => m.GroupID)
#Html.DropDownListFor(m => m.GroupID, Model.GroupList, "Please select")
#Html.ValidationMessageFor(m => m.GroupID)
// checkboxlist
#Html.EditorFor(m => m.Permissions)
<input type="submit" value="Create" />
}
The controller methods would then be
public ActionResult Create()
{
var groups = db.tm_grp_group;
var permissions = db.tm_perm_level;
GroupPermissionVM model = new GroupPermissionVM
{
GroupList = new SelectList(groups, "grp_id", "grp_name"),
Permissions = permissions.Select(p => new PermissionVM
{
ID = p.perm_id,
Name = p.perm_levelname
}
};
return View(model);
}
[HttpPost]
public ActionResult Create(GroupPermissionVM model)
{
if (!ModelState.IsValid)
{
var groups = db.tm_grp_group;
model.GroupList = new SelectList(groups, "grp_id", "grp_name");
return View(model);
}
// map the view model to a new instance of your data model(s)
// note: to get the ID's of the selected permissions -
// var selectedPermissions = model.Permissions.Where(p => p.IsSelected).Select(p => p.ID);
// save and redirect
}
Side note: I strongly recommend you follow normal naming conventions
Edit
Based on OP's comment for an option using radio buttons to select only one item, the revised code would be
public class PermissionVM
{
public int ID { get; set; }
public string Name { get; set; }
}
public class GroupPermissionVM
{
public int GroupID { get; set; }
public int PermissionID { get; set; }
public IEnumerable<SelectListItem> GroupList { get; set; }
public IEnumerable<PermissionVM> Permissions { get; set; }
}
and the view would be (no separate EditorTemplate required)
#model GroupPermissionVM
....
#using (Html.BeginForm())
{
// dropdownlist as above
// radio buttons
foreach (var permission in Model.Permissions)
{
<label>
#Html.RadioButtonForm(m => m.PermissionID, permission.ID)
<span>#permission.Name</span>
</label>
}
<input type="submit" value="Create" />
}
and in the POST method, the value of model.PermissionID will contain the ID of the selected Permission.

DropDownList MVC returns null

I've encountered some problems using DropDownList in ASP.NET MVC lately.
I want to save value of selected item to member called Wydzialy.
Sorry for not translating some names, they doesn't matter I think :)
Here is what I have:
View:
<div class="form-group">
#Html.LabelFor(model => model.Wydzial, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(x => x.Wydzial, (List<SelectListItem>)ViewBag.Wydzialy)
</div>
</div>
Model:
public class Student
{
public int Id { get; set; }
public int NumerIndeksu { get; set; }
public string Imie { get; set; }
public string Nazwisko { get; set; }
public int Semestr { get; set; }
public virtual Wydzial Wydzial { get; set; }
}
Controller:
public ActionResult Create()
{
var wydzialy = db.Wydzialy.ToList();
var lista = wydzialy.Select(W => new SelectListItem()
{
Text = W.Nazwa
}).ToList();
ViewBag.Wydzialy = lista;
return View();
}
Your trying to bind the dropdown to a complex object. A <Select> only posts backs a value type (in your case the text of the selected option).
Either bind to a property of Wydzial
#Html.DropDownListFor(x => x.Wydzial.Nazwa, (List<SelectListItem>)ViewBag.Wydzialy)
or preferably use a view model that includes a property to bind to and the SelectList
public class StudentVM
{
public int Id { get; set; }
// Other properties used by the view
public string Wydzial { get; set; }
public SelectList Wydzialy { get; set; }
}
Controller
public ActionResult Create()
{
StudentVM model = new StudentVM();
model.Wydzialy = new SelectList(db.Wydzialy, "Nazwa", "Nazwa")
return View(model );
}
View
#model StudentVM
....
#Html.DropDownListFor(x => x.Wydzial, Model.Wydzialy)
Note you appear to be binding only to the Nazwa property of Wydzial. Typically ou would display a text property but bind to an ID property.

Create ActionResult for save throws error saying The model item passed into the dictionary is of type

Although this error is very common in the forum, but i am not able to understand how to fix it in my project. I am new to MVC framework.
View code:-
#model ClassifiedProject.Models.CreateAdvertVM
<div class="editor-label">#Html.LabelFor(model => model.AdvTitle) <i>(E.g. Old Samsung Galaxy Tab 2)</i></div>
<div class="editor-field">
#Html.EditorFor(model => model.AdvTitle)
#Html.ValidationMessageFor(model => model.AdvTitle)
</div>
<div class="editor-label">#Html.LabelFor(model => model.AdvDescription)</div>
<div class="editor-field">
#Html.TextAreaFor(model => model.AdvDescription)
#Html.ValidationMessageFor(model => model.AdvDescription)
</div>
<div class="editor-label">#Html.Label("Advertisement Category")</div>
<div class="editor-label">
#Html.DropDownListFor(model => model.SelectedCategoryId, Model.Categories, new { #class = "ddlcs" })
#Html.ValidationMessageFor(model => model.SelectedCategoryId)
</div>
<p><input type="submit" value="Save" /></p>
Controller code of Save button actionresult:-
[HttpPost]
public ActionResult Create(TR_ADVERTISEMENT tr_advert)
{
if (ModelState.IsValid)
{
tr_advert.CreatedDate = tr_advert.ModifiedDate = DateTime.Now;
if (tr_advert.IsPriceOnRequest)
{
tr_advert.CurrencyID = 0;
tr_advert.Price = 0;
}
db.ADVERTISEMENT.Add(tr_advert);
db.SaveChanges();
return RedirectToAction("Index");
}
Controller code for the form in render stage:-
// GET: /Advert/Create
public ActionResult Create()
{
var model = new CreateAdvertVM();
ViewBag.Message = "Post New Advertisement.";
////Render Category DDL
var cat = from s in db.CategoryDbSet
where s.IsActive == true
orderby s.CatName
select new { s.CatID, s.CatName };
var catListItems = cat.ToList().Select(c => new SelectListItem
{
Text = c.CatName,
Value = c.CatID.ToString()
}).ToList();
catListItems.Insert(0, new SelectListItem { Text = "[--Select the category--]", Value = "" });
model.Categories = catListItems;
return View(model);
ViewModel inherited from EF class:-
[NotMapped]
public class CreateAdvertVM : TR_ADVERTISEMENT
{
[DisplayName("Category")]
[Required]
public int? SelectedCategoryId { get; set; }
public IEnumerable<SelectListItem> Categories { get; set; }
}
EF Model:-
public class TR_ADVERTISEMENT
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int AdvID { get; set; }
[Required]
[DisplayName("Sub Category")]
public int SubCatID { get; set; }
public int CurrencyID { get; set; }
[DisplayName("Price on request")]
public bool IsPriceOnRequest { get; set; }
[DisplayName("Posted Date")]
[DisplayFormat (DataFormatString="{0:dd-MM-yyyy}")]
public Nullable<System.DateTime> CreatedDate { get; set; }
public Nullable<System.DateTime> ModifiedDate { get; set; }
}
On the save button click, i have to save the data into the tr_advertisement table using the EF model.
Please suggest the solution to this problem.
It is the model type you are passing into your Create ActionMethod.
public ActionResult Create(TR_ADVERTISEMENT tr_advert)
should be
public ActionResult Create(CreateAdvertVM tr_advert)
I am assuming that if your model is not valid, you are passing it back further down in your action result (which you are not showing), such as
Return View(tr_advert)
But, you are passing the wrong model type at that point for that view.
EDIT
I would also update your view model so that instead of inheriting from the EF class, simply include the EF class as a property.
public class CreateAdvertVM
{
[DisplayName("Category")]
[Required]
public int? SelectedCategoryId { get; set; }
public IEnumerable<SelectListItem> Categories { get; set; }
public TR_ADVERTISEMENT tr_advert{get;set;}
}
This will make it so that your save code in the Create method can still be used with only minor modifications
[HttpPost]
public ActionResult Create(CreateAdvertVM model)
{
if (ModelState.IsValid)
{
model.tr_advert.CreatedDate = model.tr_advert.ModifiedDate = DateTime.Now;
if (model.tr_advert.IsPriceOnRequest)
{
model.tr_advert.CurrencyID = 0;
model.tr_advert.Price = 0;
}
db.ADVERTISEMENT.Add(model.tr_advert);
db.SaveChanges();
return RedirectToAction("Index");
}

Resources