MVC Partial View with List Model - c#

I have a news item page and I would like to include a partial view presenting a list of the latest 5 news items. However, I get a
this dictionary requires a model item of type
'System.Collections.Generic.List
error message. I'm assuming the main view is strong typed to present a single item, whereas the partial view is strongly typed with a generic list and the two don't match.
Controller
public ActionResult NewsItem(int newsId, string newsTitle)
{
var q = _ctx.tblNews.Single(x => x.newsID == newsId);
return View(q);
}
public ActionResult NewsLatest()
{
var q = _ctx.tblNews.OrderBy(x => x.newsCreateDate)
.Where(x => x.WebsiteID == 2 && x.newsPublish).Take(5).ToList();
return View(q);
}
View (simplified)
#using MyMVC.Models
#model tblNews
<h2>#Model.newsTitle</h2>
#Html.Raw(Model.newsText)
<p>#Model.newsCreateDate</p>
#Html.RenderPartial("NewsLatest")
Partial View
#using MyMVC.Helpers
#model List<MyMVC.Models.tblNews>
<table>
#foreach (var x in Model)
{
<tr>
<td>
#Html.ActionLink(x.newsTitle, "NewsItem", new { newsId = x.newsID, newsTitle = x.newsTitle.ToSeoUrl() })<br />
<hr />
</td>
</tr>
}
</table>
I tried this in the view:
#{
Html.RenderPartial("NewsLatest", new List<tblNews> { new tblNews()});
}
but it looks like the ActionResult doesn't get fired trying i this way.
There are a few questions on SO relating to this. But I just can't get my head around the solutions. The penny isn't dropping!
Do I really have to create a view model that incorporates the news item data and the list of news items?

Related

Generate a list of contacts from a table based on a Bool value

I am currently developing an asp.NET MVC web application as a front end to a database. I have a MySQL database, one of the tables is contact information for employees. I have added a column of 'isOnSite' of datatype TINYINT(1).
I have updated the data model in my application, and added a checkbox control for this in one of my view. This works fine, I edit a contact, check the box to say that they are currently contracted to this particular site, and a '1' is populated in the 'isOnSite' column for that particular record, great!
One of the views is a Dashboard. In this view (using a partial view) I would like to generate a list of the contacts in the table that have the value of 'isOnSite = true'
I am struggling to do this. I should mention that I am pretty new to all of this.
Any help would be greatly appreciated.
Thank you in advance!
-- EDIT --
With the help of #Bunnynut and also my Father-in-Law We managed to solve this,
in large part to the code examples by #Bunnynut.
CONTROLLER ACTION
public ViewResult Index()
{
var tblcontacts = from m in db.tblcontacts.Where(x => x.isOnSite)
select m;
return View(tblcontacts.OrderBy(x => x.LastName).ToList());
}
PARTIAL VIEW
#model IEnumerable<ResourceBase.Models.tblcontact>
#{
ViewBag.Title = "OnSite";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>OnSite</h2>
<div class="container-fluid-viewport jumbotron col-xs-offset-4">
#foreach (var x in Model)
{
#x.FullName <br />
}
</div>
And the Main view just renders the partial view.
Thanks again for your help #Bunnynut
To begin with the Index View uses a model of type ResourceBase.Models.tlbcontact which is it seems a single tblcontcat.
But you are try to pass a List to your partial view so that is not possible.
You Index View should consume a model of which it pass parts of it to your partial views.
IndexViewModel:
public class IndexViewModel
{
public string Title { get; set; }
public List<ResourceBase.Models.tblcontact> Employees { get; set; }
}
Index Action:
public ActionResult Index()
{
var model =
new IndexViewModel
{
Title = "PeopleBase Dashboard",
Employees = GetEmployees()
};
return View(model);
}
Index View:
#model IndexViewModel
<div class="container-fluid-viewport col-md-offset-3 col-md-9">
<h2>#Model.Title</h2>
<div class="partialViewWrapper jumbotron">#{Html.RenderPartial("_peopleBaseDashView", Model.Employees.Where(x => x.isOnSite).ToList());}
</div>
</div>
Your partial View looks ok to me
Use the following SELECT statement
SELECT * FROM yourTable WHERE isOnSite = 1
to fill a property of your ViewModel, which you can then display in your view.
You can easily pass the list of employees directly to your partial view as long as you tell your partial view the model passed is of that type.
So in the master view containing the code that call the partial view you can do this:
#{Html.RenderPartial("NAME_PARTIAL_VIEW", employees.Where(x => x.isOnSite).ToList());}
And in you partial view:
#model List<Employee>
#foreach(var employee in Model)
{
#employee.Name
}

How do I display all elements of a ViewModel in a view

I know that you can do #Html.DisplayForModel and ValueForModel, is there anything similar the viewModel? I want to display all the property names and the property values for the elements in my viewmodel
My regualr ValueFor is not working either
Heres what I have so far...although, its not working:
#model BookStore.ViewModels.CheckOutViewModel
#using (Html.BeginForm())
{
<h2>Checkout Summary</h2>
<fieldset>
<legend>Payment Information</legend>
#Html.ValueFor(m => m.CreditCard1)<br />
#Html.ValueFor(m => m.CreditCardType1) <br />
#Html.DisplayNameFor(Model => Model.CreditCard1)
#Html.DisplayFor(Model => Model.CreditCard1)
#Html.
</fieldset>
<fieldset>
<legend>Shipping Information</legend>
#Html.ValueForModel()
</fieldset>
}
Controller Code:
public ActionResult Complete(int? id)
{
return View(id);
}
Edit: I guess my real question is how to get ANY property from my view model to show its value
You're passing an Id of the object (model) to your view instead of the model itself. You should have somthing like this in your controller:
public ActionResult Complete(int? id)
{
CheckOutViewModel model = GetModel(id);
return View(model);
}
Where GetModel is some function where you retrieve the CheckOutViewModel based on the id.
EDIT: Looking at your controller you are just returning an integer not your model
You could do something like this if you really are new to this:
public ActionResult Complete(int? id)
{
if(int != null && int != 0){ //Makes sure int isn't empty or 0
var allModelData = dataContext(); //Get all the data in your dataContext
var myModel = allModelData.FirstOrDefault(x => x.id == id); //This gets the
//model that matches the ID
CheckOutViewModel viewModel = myModel; //Populate it into your viewmodel
return View(viewModel); //Return your viewmodel
}
return View();
}
This isn't the most efficient way of doing things but will get you started with some data.
I would also recommend using Model.PropertyName as ValueFor does a simple render which ignores any templates (Equivalent of calling String.Format)
#Model.CreditCard1<br />
#Model.CreditCardType1 <br />

MVC Shared Partial Views using different controllers and models

I have 2 controllers that generate 2 index views.
What i would like to do is use these views as global shared partial views but cant seem to get this working.
Does anyone know if this is even possible?
My controller code is
public ActionResult Index()
{
var viewModel = (from P in db.Projects
join R in db.Reports on P.ProjectTitle equals R.ReportProjectID into ps
from R in ps.DefaultIfEmpty()
select new MyViewModel { Project = P, Report = R });
return View(viewModel);
}
My ViewModel code is
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace MiLife2.ViewModels
{
public class MyViewModel
{
public Project Project { get; set; }
public Report Report { get; set; }
}
}
and my view is
#model IQueryable<MiLife2.ViewModels.MyViewModel>
#{
ViewBag.Title = "Index";
}
enter code here
<h2>Index</h2>
<div>#Html.Partial("_Partial1")</div>
<p>
#Html.ActionLink("Create New", "Create")
</p>
<table>
<tr>
<th></th>
</tr>
#foreach (var item in Model)
{
<tr>
<td>#item.Project.ProjectTitle </td>
<td>#item.Project.ProjectCreatedByID</td>
<td>#item.Project.ProjectCreatedDate</td>
<td>#if (item.Report == null)
{
<text>No Reports</text>
}
else
{
#item.Report.Title;
}
</td>
<td>#if (item.Report == null)
{
<text> </text>
}
else
{
#item.Report.Description;
}</td>
<td>
#Html.ActionLink("Edit", "Edit", new { id=item.Project.ProjectID }) |
#Html.ActionLink("Details", "Details", new { id=item.Project.ProjectID }) |
#Html.ActionLink("Delete", "Delete", new { id=item.Project.ProjectID })
</td>
</tr>
}
</table>
If i create a partial page and paste the above view into it and then use #HTML.Partial("_ProjPartial") i get the error
The model item passed into the dictionary is of type 'System.Collections.Generic.List1[MiLife2.Project]', but this dictionary requires a model item of type 'System.Linq.IQueryable1[MiLife2.ViewModels.MyViewModel]'.
This does not happen if i use #HTML.Partial("_ProjPartial") from within the Index cshtml page in the specific controller views folder.
From the error it looks like to me that your partial view is looking for the same model as you have on your view. Passing the model to your partial should fix that error
#Html.Partial("_Partial1", Model)
update:
since that didn't work for you I would try using an ajax call
$('.btnSubmit').on('click', function(){
$.ajax({
url: "#(Url.Action("Action", "Controller"))",
type: "POST",
cache: false,
async: true,
data: { id: id },
success: function (result) {
$(".Content").html(result);
}
});
});
then in your controller
public PartialViewResult GetPartial()
{
var viewModel = (from P in db.Projects
join R in db.Reports on P.ProjectTitle equals R.ReportProjectID into ps
from R in ps.DefaultIfEmpty()
select new MyViewModel { Project = P, Report = R });
return PartialView("_Partial1", viewModel);
}
Using this ajax call you can call the partial view from any view and you can pass different id's, on button clicks or as needed to refresh the view. Hopefully calling it this way will fix your error. let me know if you have any questions.
Recently ran into something similar, so I wanted to add my 2 cents. The answer for me was in what I was passing to the Partial View.
I was attempting to pass a string to a partial view, but when that string happened to be null, it was acting as if I had not passed anything into the Partial, which means it defaulted to passing the current view's model.
For example, I have a view which renders a partial and that partial takes in a string:
#model SomeModel
#{ Html.RenderPartial("_MyPartialView", SomeModel.StringProperty) }
If SomeModel.StringProperty happens to be null, then it is going to try and pass what ever the current view's model is (which, in this case is SomeModel). So instead, I simply wrote the following which will pass in an empty string if SomeModel.StringProperty happens to be null:
#model SomeModel
#{ Html.RenderPartial("_MyPartialView", SomeModel.StringProperty ?? string.Empty) }
Hope this helps someone.

Update a Model and a Collection of Models on a Single View (MVC4)

I've been working on an MVC 4 Application and have run into a problem when attempting to update Models in a ViewModel.
My ViewModel (detailed below) contains one ComplexObjectOne and a List<ComplexObjectTwo>.
My GET ActionResult successfully populates the ViewModel from a database and everything displays correctly on my View.
The problem is encountered when attempting to pass the ComplexObjectOne and List<ComplexObjectTwo> to the POST ActionResult.
The ComplexObject is passed correctly but everything I've tried fails pass the List<ComplexObjectTwo> collection.
My ComplexModelOne Model
public class Test
{
public int Id {get;set;}
public string Result {get;set;}
public virtual ICollection<TestResult> TestResults {get;set;}
}
My ComplexModelTwo Model
public class TestResult
{
public int Id {get;set;}
public string Result {get;set;}
public string Comment {get;set;}
public virtual Test Test{get;set;}
}
My ViewModel
public class TestingViewModel
{
public TestingViewModel()
{
if(TestResults == null)
{
TestResults = new List<TestResult>();
}
}
public Test Test {get;set;}
public IEnumerable<TestResult> TestResults {get;set;}
}
My Edit() GET ActionResult
public ActionResult Edit(int id = 0)
{
var viewModel = new TestingViewModel();
Test test = testRepo.GetTestById(id);
var results = test.TestResults;
viewModel.Test = test;
viewModel.TestResults = results;
return View(viewModel);
}
My Edit() POST ActionResult
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(TestingViewModel model)
{
// do update - left out for brevity
}
My Edit.cshtml View
#model Namespace.Models.ViewModels.TestingViewModel
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
#Html.EditorFor(model => model.Test, "TestHeader")
<table>
<tr>
<th>Test</th>
<th>Result</th>
<th>Comment</th>
</tr>
#Html.EditorFor(model => model.TestResults, "TestResults")
</table>
<input type="submit" value="Update"/>
}
Within my View I do use a couple of EditorTemplates to display the property fields.
Any assistance, comments, or suggestions will be much appreciated. I'd like to be able to accomplish updating these entities on a single page instead of multiple pages which I resorted to in the Create() steps.
Thank you,
Patrick H. (stpatrck)
Replace:
#Html.EditorFor(model => model.TestResults, "TestResults")
with:
#Html.EditorFor(model => model.TestResults)
and then rename your EditorTemplates/TestResults.cshtml editor template to EditorTemplates/TestResult.cshtml (notice the missing s) and inside replace the model declaration from:
#model IEnumerable<TestResult>
to:
#model TestResult
Now obviously this will lead to getting rid of any for or foreach loops you might have written in this editor template because now ASP.NET MVC will automatically invoke the template for each element of the collection.
So for example:
#foreach (var item in Model)
{
#Html.EditorFor(x => item.SomeProperty)
}
will simply become:
#Html.EditorFor(x => x.SomeProperty)
Now look at the generated markup and notice the difference in the names of your input fields. Before you had:
<input type="text" name="item.SomeProperty" value="foo" />
and now you have:
<input type="text" name="TestResults[0].SomeProperty" value="foo" />
Now when you submit the form to the POST action the default model binder will be able to successfully bind the collection because now the naming convention is respected. You can read more about this convention in the following blog post.
Also you have circular references in your object graph which cannot be successfully serialized and model bound. You should use view models in order to break this circular dependency.

Holding values of the search after refreshing the page

I have asked this question, I didn't get my answer. So I did much more researches about this and still couldn't find a proper good answer for this.
My controller (shortened):
public ActionResult SearchResult(String sortOrder, String carMake, String carModel)
{
var cars = from d in db.Cars
select d;
if (!String.IsNullOrEmpty(carMake))
{
if (!carMake.Equals("All Makes"))
{
cars = cars.Where(x => x.Make == carMake);
}
}
if (!String.IsNullOrEmpty(carModel))
{
if (!carModel.Equals("All Models"))
{
cars = cars.Where(x => x.Model == carModel);
}
}
switch (sortOrder)
{
case "Model":
cars = cars.OrderBy(s => s.Model);
break;
default:
cars = cars.OrderBy(s => s.Make);
break;
}
return View(cars);
}
My Index view (shortened - this is where user filters cars by different inputs):
#model IEnumerable<Cars.Models.Car>
#using (Html.BeginForm("SearchResult", "Home", FormMethod.Post))
{
<label>Make</label>
<select id="MakeID" name="carMake">
<option>All Makes</option>
</select>
<label>Model</label>
<select id="ModelID" name="carModel">
<option>All Models</option>
</select>
<button type="submit" name="submit" value="search" id="SubmitID">Search</button>
}
My SearchResult view where shows the search results (shortened):
#model IEnumerable<Cars.Models.Car>
<table>
#foreach (var item in Model)
{
<tr>
<td>
<label>Make:</label>
<p>#Html.DisplayFor(modelItem => item.Make)</p>
</td>
<td>
<label>Model:</label>
<p>#Html.DisplayFor(modelItem => item.Model)</p>
</td>
</tr>
}
</table>
Model
My goal: When user clicks on the sort by Model the page will sort the results by Model.
Problem: When the sortby is clicked, all the parameters of action SearchResult will be null since the search value do not exist in SearchResult View.
Question: How to fix this?
Thanks, Amin
UPDATE: Any example would be deeply appreciated. I'm stuck with back-end the whole process of sending and fetching data between controller and view.
You can create one class like below
public CarFilter CarFilter;
public byte PageSize;
public short PageNumber;
public int TotalRows;
where CarFilter is another class where you can store your filters enter by users.
and use this class as a model for your view.so that when you view is loading you can load car filter data.
You can retain your search filters by doing any of the following:
Use sessions to store the filters on your search result view. This way, when you post on the same view using Sort you would still have the search filters.
(I prefer this one) Wrap your model in a class that has properties for your search filters. This way, you will still be able to retrieve the search filters. Provided that you have a POST for SearchResult that accepts the Wrapper Model as parameter.
Create Classes as follow:
class ManageCarSearch
{
public CarFilter CarFilter;
public byte PageSize;
public short PageNumber;
public int TotalRows;
public String sortOrder;
}
Class CarFilter
{
public String carMake,
public String carModel
}
your searchResult view look like below:
#model IEnumerable<car>
<table>
#foreach (var item in Model)
{
<tr>
<td>
<label>Make:</label>
<p>#Html.DisplayFor(modelItem => item.Make)</p>
</td>
<td>
<label>Model:</label>
<p>#Html.DisplayFor(modelItem => item.Model)</p>
</td>
</tr>
}
</table>
Model
and controller as below:
public ActionResult SearchResult(String sortOrder, String carMake, String carModel)
{
var cars = from d in db.Cars
select d;
ManageCarSearch objsearch = new ManageCarSearch();
////your Logic
return View(objsearch);
}
and you serach view look like below:
#model IEnumerable<ManageCarSearch>
#using (Html.BeginForm("SearchResult", "Home", FormMethod.Post))
{
<label>Make</label>
<select id="MakeID" name="carMake">
<option>All Makes</option>
</select>
<label>Model</label>
<select id="ModelID" name="carModel">
<option>All Models</option>
</select>
<button type="submit" name="submit" value="search" id="SubmitID">Search</button>
}

Categories

Resources