Problem in showing ViewModel in Create form - asp.net-mvc

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)
}

Related

Dropdown list population from ViewModel

First of all, I know this question has been asked many, many times. I've read countless articles and Stack Overflow answers. I've tried to figure this problem out for four days and I think I need help if someone doesn't mind.
I have two databases. The employee database has a field called "DisplayName" -- the second database has a relationship with the first and they work together great. I'm able to call the two databases perfectly in another application.
You can see the in the picture Index Page
that I have a list of people. I want a dropdown below it that lists all display names in the database so employees can add themselves to the list. You'll see a dropdown in the image but it's not populated.
Seems simple. But geez. Part of a problem I'm having is my home controller already has a function to populate the list in the picture so I can't do another on that page. I've tried a lot of suggestions on a lot of sites. I get IEnumerable errors or display reference errors....
Here's my controller (again - it has nothing in it that helps the dropdown):
namespace SeatingChart.Controllers
{
public class HomeController : Controller
{
private ApplicationDbContext db = new ApplicationDbContext();
// GET: Employee
public ActionResult Index()
{
var lists = db.BreakModels
.Include("Employee")
.Include("TimeEntered")
.Include("TimeCleared")
.Include("DisplayName")
.Select(a => new HomeIndexViewModels
{
Employee = a.Employee,
DisplayName = a.EmployeeModels.DisplayName,
TimeEntered = a.TimeEntered,
TimeCleared = a.TimeCleared.Value,
Id = a.EmployeeModels.Id,
});
return View(lists);
}
View:
#model IEnumerable<SeatingChart.Models.HomeIndexViewModels>
#{
Layout = null;
}
#Html.Partial("_Header")
<div class="container_lists">
<div class="container_break col-md-8">
<h5 style="text-align:center">Break List</h5>
<table class="table-bordered col-lg-12">
#if (Model != null)
{
foreach (var item in Model)
{
if (item.TimeCleared == null)
{
<tr>
<td>
#Html.DisplayFor(modelItem => item.DisplayName)
</td>
<td>
 BV
</td>
<td>
 #item.TimeEntered.ToString("HH:mm")
</td>
</tr>
}
}
}
</table>
#using (Html.BeginForm())
{
<div class="row site-spaced">
<div class="col-3">
#Html.DropDownList("DisplayName", new SelectList(new List<string>() { "---Dispatcher---" }), new { #class = "required " })
</div>
</div>
<div class="col-3">
<input type="submit" value="Submit" class="site-control" />
</div>
}
</div>
</div>
ViewModel:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Html;
namespace SeatingChart.Models
{
public class HomeIndexViewModels
{
//Break Model
public int BreakId { get; set; }
public int Employee { get; set; }
public DateTime TimeEntered { get; set; }
public DateTime? TimeCleared { get; set; }
//Employee Model
public int Id { get; set; }
public string DisplayName { get; set; }
public string DisplayNames { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public bool NotActive { get; set; }
public int Force { get; set; }
public string EmployeeList { get; set; }
}
}
I hope this is clear enough. I've tried so many different ways with so much code - the errors are different with everything I've tried.
Thanks in advance for your patience and help!
You can add to your viewmodel
public List<SelectListItem> Employees { get; set; }
Then you can populate this list with controller then in view just call it with:
#Html.DropDownListFor(m => m.Id, Model.Employees, new { #class = "form-control", required = "required" })
Update - how to populate list. Should work (but not tested code).
public List<SelectListItem> GetEmployeeForDropdown(List<HomeIndexViewModels> list)
{
List<SelectListItem> empList = new List<SelectListItem>();
try
{
if (list != null && list.Count > 0)
{
foreach (var item in list)
{
empList.Add(new SelectListItem { Text = item.DisplayName, Value = item.Id.ToString() });
}
}
else
{
empList.Add(new SelectListItem { Text = "No items", Value = string.Empty });
}
}
catch (Exception ex)
{
//handle exceptions here
}
return empList;
}
Edit: Remember to use your model in view!

Create View with Model and List<Model>

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>
}

MVC Partial View throwing error on dropdownfor

I have a partial view I want to display as a search interface. Every time it tells me I get the error
There is no ViewData item of type IEnumerable that has the key resource_type_id.
I have tried so many things to make this work but nothing helps.
This is my view calls the partialview
#model IEnumerable<NewSAMACentral2._0.ViewModel.MemberResourcesViewModel.MemberResource>
#{
ViewBag.Title = "Add Resource To Folder";
}
<h2>Add Resource To Folder</h2>
<div>
#{Html.Partial("SearchResource", new NewSAMACentral2._0.ViewModel.MemberResourcesViewModel.ResourceSearch());}
</div>
#using (Ajax.BeginForm("InsertAttendee", "Meetings", new AjaxOptions { HttpMethod = "POST" }))
{
if (Model.Any())
{
}
}
This is my partialview
#model NewSAMACentral2._0.ViewModel.MemberResourcesViewModel.ResourceSearch
#using (Ajax.BeginForm("AddAttendee", "Meetings", new AjaxOptions { UpdateTargetId = "AddAttendee", HttpMethod = "POST" }))
{
<div class="form-group">
<label for="keyword">Keyword(s): </label>#Html.TextBox("keyword", null, new { #class = "form-control" })
</div>
<div class="form-group">
#Html.LabelFor(model => Model.resource_type_id)
#Html.DropDownListFor(model => Model.resource_type_id, Model.resource_type, "-- Select --", new { #class = "form-control" })
</div>
<div class="form-group">
<label for="author">Author(s): </label>#Html.TextBox("author", null, new { #class = "form-control" })
</div>
<div class="form-group">
<input type="submit" name="submit" value="Search" />
</div>
}
This is the controller that never seems to get called...
public PartialViewResult SearchResource()
{
var results = new MemberResourcesViewModel.ResourceSearch();
results.resource_type = db.Resource_Types.Select(s => new SelectListItem
{
Text = s.name,
Value = s.id.ToString()
}).Distinct().OrderBy(x => x.Text);
return PartialView(results);
}
This is the ViewModel
namespace NewSAMACentral2._0.ViewModel
{
public class MemberResourcesViewModel
{
public IEnumerable<MemberResource> MemberResourceResult;
public MemberResource memberResource;
public class MemberResource
{
public ResourceSearch resource_search { get; set; }
[Key]
public int Id { get; set; }
[DisplayName("Resource Title")]
public string title { get; set; }
public IEnumerable<SelectListItem> resource_type { get; set; }
[DisplayName("Resource Type")]
public string resource_type_id { get; set; }
[DisplayName("Keyword")]
public string keyword { get; set; }
[DisplayName("Author")]
public string author { get; set; }
[DisplayName("Subject Type")]
public string subject_type { get; set; }
[DisplayName("Industry")]
public string industry { get; set; }
[DisplayName("Description")]
public string description { get; set; }
}
public class ResourceSearch
{
[DisplayName("Author")]
public string author { get; set; }
public IEnumerable<SelectListItem> resource_type { get; set; }
[DisplayName("Resource Type")]
public string resource_type_id { get; set; }
[DisplayName("Keyword")]
public string keyword { get; set; }
}
}
}
You have to be carefull with capital letters and put model not Model:
#Html.LabelFor(model => model.resource_type_id)
#Html.DropDownListFor(model => model.resource_type_id, model.resource_type, "-- Select --", new { #class = "form-control" })
On your main View Action (not partial view action result):
var types = db.Resource_Types.Distinct().OrderBy(x => x.Text).ToList();
SelectList typeList = new SelectList(types, "ID", "Name");
ViewBag.Types = typelist;
All that was done there was pull your objects from the DataBase. Then we turned that into a Select list with 'ID' as the value field and 'Name' as the text field. Then we put that select list in a viewbag to be used by our view.
Next In your partial view:
#Html.DropDownListFor(model => model.resource_type_id, new SelectList(ViewBag.Types, "value", "text"), "-- Select --", new { #class = "form-control" })
The only difference in this HTML is its pulling values from the select list so you never have to worry about it even hitting the partial view controller. I also changed the capital 'M' in model to a lowercase because not needed
Remember to put the code for the ViewBag in your main actionresult, not the partial view action result.
As Stephen commented below all you really need is :
#Html.DropDownListFor(model => model.resource_type_id, (SelectList)ViewBag.Types, "-Select-", ...)
Your error occurs because the value of Model.resource_type is null when used inside the DropDownListFor() method.
In your main view you use Html.Partial() to render a partial view named SearchResource.cshtml, passing it a new instance of your class ResourceSearch. But ResourceSearch does not have a default constructor which initializes the resource_type property so its null, hence the exception.
Your naming conventions and use of nested models make it difficult to understand, and you have not shown the GET method for the main view, but I suspect you are wanting to actually call the SearchResource() method on your controller which will return the partial view of the form. In which case you need to use
#{Html.RenderAction("SearchResource")}
which will call the method and return its partial. Since that method initializes a new instance of ResourceSearch and populates its resource_type property, it will no longer be null
Note you should also consider applying the [ChildActionOnly] attribute to the method so it cant be called by the user entering the url in the address bar.

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