MVC3 error when passing a model from controller to view while using a viewModel - asp.net-mvc-3

I'm sorry, for I'm sure there's a way to do this with a viewModel, however I'm very inexperienced with this and don't even know if I'm doing it correctly.
What I'm trying to do is pass multiple blogs and the profile info of the user who posted each blog to a view.
I'm getting the following error.
The model item passed into the dictionary is of type
'ACapture.Models.ViewModels.BlogViewModel', but this dictionary
requires a model item of type
'System.Collections.Generic.IEnumerable`1[ACapture.Models.ViewModels.BlogViewModel]'.
I'm trying to pass the following query results to the view.
var results = (from r in db.Blog.AsEnumerable()
join a in db.Profile on r.AccountID equals a.AccountID
select new { r, a });
return View(new BlogViewModel(results.ToList()));
}
This is my viewModel
public class BlogViewModel
{
private object p;
public BlogViewModel(object p)
{
this.p = p;
}
}
And my view
#model IEnumerable<ACapture.Models.ViewModels.BlogViewModel>
#{
ViewBag.Title = "Home Page";
}
<div class="Forum">
<p>The Forum</p>
#foreach (var item in Model)
{
<div class="ForumChild">
<img src="#item.image.img_path" alt="Not Found" />
<br />
<table>
#foreach (var comment in item.comment)
{
<tr><td></td><td>#comment.Commentation</td></tr>
}
</table>
</div>
}
</div>
Thanks in advance.

I guess you need to change your view model a little to:
public class BlogViewModel
{
public Blog Blog { get; set; }
public Profile Profile{ get; set; }
}
and then return it as follow:
var results = (from r in db.Blog.AsEnumerable()
join a in db.Profile on r.AccountID equals a.AccountID
select new new BlogViewModel { Blog = r, Profile = a });
return View(results.ToList());
Then in your foreach loop inside of view, you will get an objects that will contain both - profile and blog info, so you can use it like f.e. #item.Profile.Username

I'm not entirely sure what you're trying to accomplish with the ViewModel in this case, but it seems like you are expecting for the page to represent a single blog with a collection of comments. In this case you should replace
IEnumerable<ACapture.Models.ViewModels.BlogViewModel>
With
ACapture.Models.ViewModels.BlogViewModel
Then Model represents a single BlogViewModel, that you can iterate over the comments by using Model.comments and access the image using Model.image.img_path.
If this not the case, and you intend to have multiple BlogViewModels per page, then you will have to actually construct a collection of BlogViewModels and pass that to the view instead.

Related

How to store join query data and retrieve using foreach loop from master layout page in asp.net mvc

I am new in asp.net mvc. I am trying to build role wise dynamic menu show in the view page. Each user can have multiple role.
I have a join query like:
My Controller looks like with join query:
var query= (from rMPageMap in db.RoleModulePageMaps
join userRole in db.UserRoleMaps
on rMPageMap.RoleId equals
join roleMaster in db.RoleMasters
on rMPageMap.RoleId equals roleMaster.Id
join modMaster in db.ModuleMaster
on rMPageMap.ModuleId equals modMaster.Id
join sModMaster in db.SubModuleMasters
on rMPageMap.SubModuleId equals sModMaster.Id
join pMaster in db.PageMasters
on rMPageMap.PageId equals pMaster.Id
where (userRole.UserId == appuser.Id &)
select new
{
rMPageMap.RoleId,
rMPageMap.PageMaster.Name,
roleMaster.Id,
roleName = roleMaster.Name,
modId = modMaster.Id,
moduleName = modMaster.Name,
subModuleId = sModMaster.Id,
subModuleName = sModMaster.Name,
pageId = pMaster.Id,
pageName = pMaster.Name,
parentPageId = pMaster.ParentPageId,
rMPageMap.AddData,
rMPageMap.EditData,
rMPageMap.ViewData,
rMPageMap.DeleteData,
rMPageMap.ShowInDashBoard,
rMPageMap.ShowInMenu
});
Session["rolemodulepage"] = query;
I find the values in the session while debugging. but i can not retrieve data from this using foreach loop in layout page.
Here is my view page that i try ro retrieve but does not work.
View Page:
#if (Request.IsAuthenticated)
{
var sessionVar = System.Web.HttpContext.Current.Session["rolemodulepage"];
foreach(var i in sessionVar) // error
{
#i.... // error
}
// So here how to retrieve data from session using foreach loop. I tried but does not work. Pls help. If you have some resource for dynamically mane show in view page pls share with me.
Can anyone explain that how to do it using showing dynamic menu by role based user in master layout page. Wihout login none can enter the site, pls explain with examples so that i can understand. Thanks in advance.
You query is generating a collection of anonymous objects which you cannot access in the view. Create a view model containing the properties you need and project your query into it, for example
public class MenuVM
{
public int RoleId { get; set; }
public string PageMasterName { get; set; }
....
}
and then modify the query to
var query = (from rMPageMap in db.RoleModulePageMaps
....
where (userRole.UserId == appuser.Id &)
select(new MenuVM()
{
RoleId = rMPageMap.RoleId,
PageMasterName = rMPageMap.PageMaster.Name,
....
}).AsEnumerable()
;
and then in the view you can cast the Session value and loop through it
var sessionVar = HttpContext.Current.Session["rolemodulepage"] as IEnumerable<MenuVM>;
if (sessionVar null)
{
foreach(var i in sessionVar)
{
....
However, as this is for generating a menu in a layout, I suggest you create move the code to a child action only method that returns a strongly typed partial view, for example in say CommonController
[ChildActionOnly]
public PartialViewResult Menu()
{
if (!Request.IsAuthenticated)
{
return null;
}
// Check if the session variable exists and if not, generate the query
// and add the result to session
return PartialView("_Menu", query);
}
and the _Menu.cshtml view would be
#model IEnumerable<MenuVM>
#foreach (var i in Model)
{
....
}
and in the layout, use
#{ Html.RenderAction("Menu", "Common"); }
to generate the html for the menu.

MVC How to pass a list of objects with List Items POST action method

I want to post a List of items to controller from Razor view , but i am getting a List of objects as null
My class structre is
Model:
List<Subjects> modelItem
class Subjects
{
int SubId{get;set;}
string Name{get;set;}
List<Students> StudentEntires{get;set;}
}
class StudentEntires
{
int StudId{get;set;}
string Name{get;set;}
int Mark{get;set;}
}
The model itself is a list of items and every items contain List of child items as well. Example model is a list of Subjects and every subject contains a List of Students, and i want to input mark for every student
My View is like
#model IList<Subjects>
#{
Layout = "~/Views/Shared/_Layout.cshtml";
}
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
if (Model.Count > 0)
{
#for (int item = 0; item < Model.Count(); item++)
{
<b>#Model[item].Name</b><br />
#foreach (StudentEntires markItem in Model[item].StudentEntires)
{
#Html.TextBoxFor(modelItem => markItem.Mark)
}
}
<p style="text-align:center">
<input type="submit" class="btn btn-primary" value="Update" />
</p>
}
}
And in controller
[HttpPost]
public ActionResult OptionalMarks(int Id,ICollection<Subjects> model)
{
//BUt my model is null. Any idea about this?
}
You're finding this difficult because you're not utilising the full power of the MVC framework, so allow me to provide a working example.
First up, let's create a view model to encapsulate your view's data requirements:
public class SubjectGradesViewModel
{
public SubjectGradesViewModel()
{
Subjects = new List<Subject>();
}
public List<Subject> Subjects { get; set; }
}
Next, create a class to represent your subject model:
public class Subject
{
public int Id { get; set; }
public string Name { get; set; }
public List<Student> StudentEntries { get; set; }
}
Finally, a class to represent a student:
public class Student
{
public int Id { get; set; }
public string Name { get; set; }
public int Grade { get; set; }
}
At this point, you have all the classes you need to represent your data. Now let's create two controller actions, including some sample data so you can see how this works:
public ActionResult Index()
{
var model = new SubjectGradesViewModel();
// This sample data would normally be fetched
// from your database
var compsci = new Subject
{
Id = 1,
Name = "Computer Science",
StudentEntries = new List<Student>()
{
new Student { Id = 1, Name = "CompSci 1" },
new Student { Id = 2, Name = "CompSci 2" },
}
};
var maths = new Subject
{
Id = 2,
Name = "Mathematics",
StudentEntries = new List<Student>()
{
new Student { Id = 3, Name = "Maths 1" },
new Student { Id = 4, Name = "Maths 2" },
}
};
model.Subjects.Add(compsci);
model.Subjects.Add(maths);
return View(model);
}
[HttpPost]
public ActionResult Index(SubjectGradesViewModel model)
{
if (ModelState.IsValid)
{
return RedirectToAction("Success");
}
// There were validation errors
// so redisplay the form
return View(model);
}
Now it's time to construct the views, and this part is particularly important when it comes to sending data back to a controller. First up is the Index view:
#model SubjectGradesViewModel
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
#Html.EditorFor(m => m.Subjects) <br />
<input type="submit" />
}
You'll notice I'm simply using Html.EditorFor, whilst passing Subjects as the parameter. The reason I'm doing this is because we're going to create an EditorTemplate to represent a Subject. I'll explain more later on. For now, just know that EditorTemplates and DisplayTemplates are special folder names in MVC, so their names, and locations, are important.
We're actually going to create two templates: one for Subject and one for Student. To do that, follow these steps:
Create an EditorTemplates folder inside your view's current folder (e.g. if your view is Home\Index.cshtml, create the folder Home\EditorTemplates).
Create a strongly-typed view in that directory with the name that matches your model (i.e. in this case you would make two views, which would be called Subject.cshtml and Student.cshtml, respectively (again, the naming is important)).
Subject.cshtml should look like this:
#model Subject
<b>#Model.Name</b><br />
#Html.HiddenFor(m => m.Id)
#Html.HiddenFor(m => m.Name)
#Html.EditorFor(m => m.StudentEntries)
Student.cshtml should look like this:
#model Student
#Html.HiddenFor(m => m.Id)
#Html.HiddenFor(m => m.Name)
#Html.DisplayFor(m => m.Name): #Html.EditorFor(m => m.Grade)
<br />
That's it. If you now build and run this application, putting a breakpoint on the POST index action, you'll see the model is correctly populated.
So, what are EditorTemplates, and their counterparts, DisplayTemplates? They allow you to create reusable portions of views, allowing you to organise your views a little more.
The great thing about them is the templated helpers, that is Html.EditorFor and Html.DisplayFor, are smart enough to know when they're dealing with a template for a collection. That means you no longer have to loop over the items, manually invoking a template each time. You also don't have to perform any null or Count() checking, because the helpers will handle that all for you. You're left with views which are clean and free of logic.
EditorTemplates also generate appropriate names when you want to POST collections to a controller action. That makes model binding to a list much, much simpler than generating those names yourself. There are times where you'd still have to do that, but this is not one of them.
Change the action method signature to
public ActionResult OptionalMarks(ICollection<Subjects> model)
Since in your HTML, it does not look like there is anything named Id in there. This isn't your main issue though.
Next, do the following with the foor loop
#for(int idx = 0; idx < Model[item].StudentEntires.Count();idx++)
{
#Html.TextBoxFor(_ => Model[item].StudentEntries[idx])
}
Possibly due to the use of a foreach loop for the StudentEntries, the model binder is having trouble piecing everything together, and thus a NULL is returned.
EDIT:
Here's an example:
Controller
public class HomeController : Controller
{
public ActionResult Index()
{
var viewModel = new IndexViewModel();
var subjects = new List<Subject>();
var subject1 = new Subject();
subject1.Name = "History";
subject1.StudentEntires.Add(new Student { Mark = 50 });
subjects.Add(subject1);
viewModel.Subjects = subjects;
return View(viewModel);
}
[HttpPost]
public ActionResult Index(IndexViewModel viewModel)
{
return new EmptyResult();
}
}
View
#model SOWorkbench.Controllers.IndexViewModel
#{
ViewBag.Title = "Home Page";
}
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
if (Model.Subjects.Any())
{
int subjectsCount = Model.Subjects.Count();
for (int item = 0; item < subjectsCount; item++)
{
<b>#Model.Subjects[item].Name</b><br />
int studentEntriesCount = Model.Subjects[item].StudentEntires.Count();
for(int idx = 0;idx < studentEntriesCount;idx++)
{
#Html.TextBoxFor(_ => Model.Subjects[item].StudentEntires[idx].Mark);
}
}
<p style="text-align:center">
<input type="submit" class="btn btn-primary" value="Update" />
</p>
}
}
When you post the form, you should see the data come back in the viewModel object.

MVC3 Persisting data from controller to view back to controller

I have a model containing a couple of lists:
[Display(Name = "Facilities")]
public List<facility> Facilities { get; set; }
[Display(Name = "Accreditations")]
public List<accreditation> Accreditations { get; set; }
I populate these lists initially from my controller:
public ActionResult Register()
{
var viewModel = new RegisterModel();
viewModel.Facilities = m_DBModel.facilities.ToList();
viewModel.Accreditations = m_DBModel.accreditations.ToList();
return View(viewModel);
}
When they get to my view they are populated with the DB records (great). I then pass the model to the partial view which displays these lists as checkboxes, ready for user manipulation (I have tried based on another suggestion using for loop instead of foreach loop, made no difference):
#model LanguageSchoolsUK.Models.RegisterModel
#foreach (var item in Model.Facilities)
{
#Html.Label(item.name);
#Html.CheckBox(item.name, false, new { id = item.facility_id, #class = "RightSpacing", #description = item.description })
}
When I submit the form and it ends up back at my controller this time calling the overloaded register function on the controller:
[HttpPost]
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
// Do stuff
}
return View(model);
}
The problem is that the model parameter containing the lists (Facilities and Accreditations) is telling me that the lists are null.
Please can somebody tell me what I am doing wrong, why aren't they populated with the collections that I originally passed through and hopefully a way of asking whick ones have been checked?
Thanks.
I have tried based on another suggestion using for loop instead of
foreach loop, made no difference
Try again, I am sure you will have more luck this time. Oh and use strongly typed helpers:
#model LanguageSchoolsUK.Models.RegisterModel
#for (var i = 0; i < Model.Facilities.Count; i++)
{
#Html.HiddenFor(x => x.Facilities[i].name)
#Html.LabelFor(x => x.Facilities[i].IsChecked, Model.Facilities[i].name);
#Html.CheckBoxFor(
x => x.Facilities[i].IsChecked,
new {
id = item.facility_id,
#class = "RightSpacing",
description = item.description // <!-- HUH, description attribute????
}
)
}
Also you will undoubtedly notice from my answer that checkboxes work with boolean fields on your model, not integers, not decimals, not strings => BOOLEANS.
So make sure that you have a boolean field on your model which will hold the state of the checkbox. In my example this field is called IsChecked but obviously you could feel absolutely free to find it a better name.

Add to model in form, then redisplay form to add more

I'm new to MVC3, but so far I have managed to get along with my code just great.
Now, I would like to make a simple form, that allows the user to input a text string, representing the name of an employee. I would then like this form to be submitted and stored in my model, in a sort of list. The form should then re-display, with a for-each loop writing out my already added names. When I'm done and moving on, I need to store this information to my database.
What I can't figure out, is how to store this temporary information, until i push it to my database. Pushing everytime I submit I can do, but this has cause me alot of headaches.
Hope you guys see what I'm trying to do, and have an awesome solution for it. :)
This is a simplified version of what I've been trying to do:
Model
public class OrderModel
{
public virtual ICollection<Employees> EmployeesList { get; set; }
public virtual Employees Employees { get; set; }
}
public class Employees
{
[Key]
public int ID { get; set; }
public string Name { get; set; }
}
View
#model OrderModel
#{
if (Model.EmployeesList != null)
{
foreach (var c in Model.EmployeesList)
{
#c.Name<br />
}
}
}
#using(Html.BeginForm())
{
#Html.TextBoxFor(m => m.Employees.Name)
<input type="submit" value="Add"/>
}
Controller
[HttpPost]
public ActionResult Index(OrderModel model)
{
model.EmployeesList.Add(model.Employees);
// This line gives me the error: "System.NullReferenceException: Object reference not set to an instance of an object."
return View(model);
}
I think you should handle this by burning the employee list into the page. Right now, you're not giving your form any way of recognizing the list.
In an EditorTemplates file named Employees:
#model Employees
#Html.HiddenFor(m => m.ID)
#Html.HiddenFor(m => m.Name);
In your view:
#using(Html.BeginForm())
{
#Html.EditorFor(m => m.EmployeesList)
#Html.TextBoxFor(m => m.Employees.Name)
<input type="submit" value="Add"/>
}
[HttpPost]
public ActionResult Index(OrderModel model)
{
if (model.EmployeesList == null)
model.EmployeesList = new List<Employees>();
model.EmployeesList.Add(model.Employees);
return View(model);
}
As an added bonus to this method, it would be easy to add ajax so the user never has to leave the page when they add new employees (You might be able to just insert a new hidden value with javascript and avoid ajax. It would depend on if you do anything other than add to your list in your post).
I think this would be a good use for TempData. You can store anything in there, kind of like the cache, but unlike the cache it only lasts until the next request. To implement this, change the action method like this (example only):
[HttpPost]
public ActionResult Index(OrderModel model)
{
dynamic existingItems = TempData["existing"];
if (existingItems != null)
{
foreach (Employee empl in existingItems)
model.EmployeesList.Add(empl );
}
model.EmployeesList.Add(model.Employees);
TempData["existing"] = model.EmployeesList;
return View(model);
}

Passing multiple values into a view

I have a view that I want to take in a thread and a comment so that way when I do the post it will have both and be able to update both.
Here is the view:
#model GameDiscussionBazzar.Data.Comment
#{
ViewBag.Title = "EditComment";
Layout = "~/Views/Shared/_EditCommentLayout.cshtml";
}
<div class="EditComment">
<h1>
Edit Comment
</h1>
#using (Html.BeginForm("EditThreadComment", "Comment"))
{
<div class="EditingComment">
#Html.EditorForModel()
#Html.Hidden("comment", Model)
#Html.HiddenFor(i => i.ParentThread)
<input type="submit" value="Save"/>
#Html.ActionLink("Return without saving", "Index")
</div>
}
</div>
I have 2 methods one is just an action result and returns the view while the other is the post that returns to the main page if it succeded.
here are the methods:
public ActionResult EditThreadComment(int commentId)
{
Comment comment = _repository.Comments.FirstOrDefault(c => c.Id == commentId);
return View(comment);
}
[HttpPost]
public ActionResult EditThreadComment(Comment comment, Thread thread)
{
var c = thread.ChildComments.FirstOrDefault(x => x.Id == comment.Id);
thread.ChildComments.Remove(c);
if (ModelState.IsValid)
{
_repository.SaveComment(comment);
thread.ChildComments.Add(comment);
_tRepo.SaveThread(thread);
TempData["Message"] = "Your comment has been saved";
return RedirectToAction("Index", "Thread");
}
else
{
TempData["Message"] = "Your comment has not been saved";
return RedirectToAction("Index", "Thread");
}
}
So again my question is How do I pass 2 parameters into the view? Or how do I pass the values of my thread?
In order to pass back multiple values, you should create a ViewModel which can hold whatever objects and values that you wish to change and (ultimately) pass it back to the view.
So, create a new model like this...(I'm not at a compiler right now, so I apologize if some of this code doesn't build).
public class PostViewModel
{
public Comment Comment { get; set; }
public Thread Thread { get; set; }
}
In your controller, you need to basically convert back and forth between your PostViewModel.
public ActionResult EditThreadComment(int commentId)
{
PostViewModel post = new PostViewModel();
Comment comment = _repository.Comments.FirstOrDefault(c => c.Id == commentId);
post.Comment = comment;
post.Thread = new Thread();
return View(post);
}
public ActionResult EditThreadComment(PostViewModel post)
{
Comment comment = post.Comment;
Thread thread = post.Thread;
// Now you can do what you need to do as normal with comments and threads
// per the code in your original post.
}
And, in your view, you will now have it strongly typed to a PostViewModel now. So, at the top...
#model GameDiscussionBazzar.Data.PostViewModel
And you'll have to just go one level deeper to get to the individual Comment and Thread objects.
You can create a ViewModel class to hold a Comment and a Thread, then pass that single view model to the view which can then access both the Comment and the Thread within it.
You can use ViewBag.Thread = myThread

Resources