MVC Shared Partial Views using different controllers and models - c#

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.

Related

Is it possible to ignore model for partial view?

I have an ASP.NET MVC view with a model binding of a List
Inside my view I have a partial view that should handle a result of a different action and should be updated after the Action finished.
But the partial view is always crash because it tries to consume the model of the main view.
#model List<planemOsIdConsumer.Models.CommonDtosRole>
#{
ViewBag.Title = "Rollen und Rechte";
}
<table>
<thead>
<tr>
<th>Your Column Header Name</th>
</tr>
</thead>
<tbody>
#foreach (var item in Model)
{
<tr>
<td>
<label>#Html.Display(item.Name)</label>
</td>
</tr>
}
</tbody>
</table>
#using (Ajax.BeginForm("Create", "Role", new AjaxOptions
{
InsertionMode = InsertionMode.Replace, //target element(#mydiv) will be replaced
UpdateTargetId = "mydiv"
}))
{
<table cellpadding="5">
<tr>
<td>Rollenname:</td>
<td><input type="text" name="Name" id="roleNameVal" placeholder="Neue Rolle" /></td>
</tr>
</table>
<br />
<label id="resultLabel"></label>
<input type="submit" value="Submit" id="btn_click" />
<div id="mydiv">
#{
Html.RenderPartial("_CreateResult");
}
</div>
}
The Partial View
#model planemosIdUi.Dto.Result
#{
ViewBag.Title = "_CreateResult";
}
#{
if (Model?.Success == true)
{
<label>Erstellt</label>
}
else if(Model?.Success == false)
{
<label>Fehler</label>
}
else
{
<label>Do something</label>
}
}
The Partial view should ignore the model from the main view if this is possible.
You want to remove the Model class, thats mentioned at the top. Then only it won't ask for the model and Don't throw an error.
But based on your code, it should be there.
Pass the nullable model in your main CSHTML page as like below
Html.RenderPartial("_CreateResult", new planemosIdUi.Dto.Result());
If you are trying to display the partial view after some action, you can use ajax for appending the partial view content to mydiv id .otherwise every time when you load the page, partial view will render.
$.ajax({
cache: false,
async: false,
type: "POST",
contentType: "application/json",
url: "/ControllerName/ActionName",
success: function (result) {
$("#mydiv").html(result);
}
});
From the controller you can return the partial view as
public ActionResult ActionName()
{
planemosIdUi.Dto.Result model=new planemosIdUi.Dto.Result();// model object to partial view
return PartialView("_CreateResult",model);
}
Your _CreateResult.cshtml requires model of type Result.cs, and you must pass the model inside Html.RenderPartial method which is the second parameter of it.
If you don't want to pass the model to partial view, you could do two things
Pass the null value and have check for null value inside partial view
Html.RenderPartial("_CreateResult", null);
You could create the empty instance of model and pass it to partial view
Html.RenderPartial("_CreateResult", new planemOsIdConsumer.Models.CommonDtosRole());

MVC Partial View with List Model

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?

Passing the same model to different views

Problem: We have a Main View and a couple of partial views. Everything uses the same model as the main view. On the submit of Main view, I am trying to render a partial view and trying to pass down the model but only the properties that are getting passed down are the editable fields on the page or view. How can I pass down the other properties?
Current workaround: The partial view needs the other data too to generate a e-mail body but as it is not passed down we are creating hidden fields to pass them down.
Main View
#using (Ajax.BeginForm("CommonSave", "Common", null, new AjaxOptions
{
HttpMethod = "Post",
InsertionMode = InsertionMode.Replace,
//OnSuccess = "submitFormSuccess",
//OnFailure = "submitFormFailure"
}, new { id = "commonForm" }))
{
<div>
<table>
<tr>
<td>
#Html.EditorFor(m => m.Name)
</td>
</tr>
<tr>
<td>
<input type ="submit" name="Common Save" />
</td>
</tr>
</table>
</div>
Controller:
public class CommonController : Controller
{
public ActionResult Index()
{
CommonModel model = new CommonModel()
{
Id = 3,
Name = "Test Name"
};
return View("Common", model);
}
[HttpPost]
public ActionResult CommonSave(CommonModel model)
{
return PartialView("CommonPartial", model);
}
public bool BinderSave(CommonModel model)
{
return true;
}
}
On load of the main view(Common) Index is called.
Upon submitting the Ajaxform on the Main view, the actionmethod CommonSave is called but the model passed to CommonSave only contains the Name and not the Id.
How can we pass that down as well without creating hidden fields or doing anything?
My actual model has a lot of fields which needs to be passed down.
instead of using an ajax form I would use an ajax call and populate the fields on success. Change your button to a button instead of submit
<input type ="button" class="btnSubmit" name="Common Save" />
then in your script tag
$('.btnSubmit').click(function () {
$.ajax({
url: '#Url.Action("CommonSave", "CommonController")',
type: "POST",
data: { Id: $('.Id').val() }
cache: false,
async: true,
success: function (result) {
//put the partial into a div on the form
$(".commonForm").html(result);
//now set the fields on the partial from the model
$('#EmailName').val('#Model.EmailName');
}
});
});
I understand the need for partial views and will tell you how I've solved this however each view should have it's own model, it just should. It's a tenet in the MVVM world:
Anyway, the partials could have a model via a interface. Define your partial view using a interface as it's model. For example:
interface:
public interface IPhone
{
string PhoneNumber ( get; set; }
}
you model:
public class MainModel : IPhone
{
public string Name
{
get { ... }
set { ... }
}
public string PhoneNumber
{
get { ... }
set { ... }
}
}
The point is that as long as the model passed to the main view implements the interface (the interface could also define a property that is another model) that the partial view depends on then all you need to do is pass the model to the partial, you may need to cast the model to the interface but technically you shouldn't have to. Let me know if this helps.

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.

Rendering partial view in ASP.Net MVC3 Razor using Ajax

The base functionality I wish to achive is that the contents of a table are updated when a dropdownlist item is selected. This will update when the user makes a new selection and retrieve new information from the database and repopulate the table.
It's also worth noting that the DropDownListFor that I want the .change() to work with is not contained within the AjaxForm but appears elsewhere on the page (admittedly in another form)
To achieve this I looked at this question: Rendering partial view dynamically in ASP.Net MVC3 Razor using Ajax call to Action which does a good job of going part the way of what I want to do.
So far, I have a controller method which handles populating a customized viewmodel for the partial view:
[HttpPost]
public ActionResult CompanyBillingBandDetails(int id = 0)
{
var viewModel = new BillingGroupDetailsViewModel();
var billingGroupBillingBands =
_model.GetAllRecordsWhere<BillingGroupBillingBand>(x => x.BillingGroupId == id).ToList();
foreach (var band in billingGroupBillingBands)
{
viewModel.BillingBands.Add(band.BillingBand);
}
return PartialView("BillingGroupDetailsPartial", viewModel);
}
The ViewModel I wish to populate each call:
public class BillingGroupDetailsViewModel
{
public List<BillingBand> BillingBands { get; set; }
}
The strongly typed model I'm using as a model for the partial view
public class BillingBandsObject
{
public int BillingBandId { get; set; }
public int RangeFrom { get; set; }
public int RangeTo { get; set; }
public Decimal Charge { get; set; }
public int BillingTypeId { get; set; }
public bool Delete { get; set; }
}
The partial view it populates and returns:
#model xxx.xxx.DTO.Objects.BillingBandsObject
<tr>
<td>
#Html.DisplayTextFor(x => x.RangeFrom)
</td>
</tr>
<tr>
<td>
#Html.DisplayTextFor(x => x.RangeTo)
</td>
</tr>
<tr>
<td>
#Html.DisplayTextFor(x => x.Charge)
</td>
</tr>
The Razor code for this section of the page:
<table>
<thead>
<tr>
<th>
Range From
</th>
<th>
Range To
</th>
<th>
Charge
</th>
</tr>
</thead>
<tbody>
#using (Ajax.BeginForm("CompanyBillingBandDetails", new AjaxOptions() { UpdateTargetId = "details", id = "ajaxform" }))
{
<div id="details">
#Html.Action("CompanyBillingBandDetails", new { id = 1 })
</div>
}
</tbody>
</table>
and finally the function I lifted almost straight from Darin's answer:
$(function () {
$('#billinggrouplist').change(function () {
// This event will be triggered when the dropdown list selection changes
// We start by fetching the form element. Note that if you have
// multiple forms on the page it would be better to provide it
// an unique id in the Ajax.BeginForm helper and then use id selector:
var form = $('#ajaxform');
// finally we send the AJAX request:
$.ajax({
url: form.attr('action'),
type: form.attr('method'),
data: form.serialize(),
success: function (result) {
// The AJAX request succeeded and the result variable
// will contain the partial HTML returned by the action
// we inject it into the div:
$('#details').html(result);
}
});
});
});
At the moment I have fought through a number of errors, currently I am faced with :
"Error executing child request for handler 'System.Web.Mvc.HttpHandlerUtil+ServerExecuteHttpHandlerAsyncWrapper'."
However, i feel my understanding of the problem as a whole may be lacking.
Any help appreciated!
This error means that there was an exception while rendering your child view. Probably something related to your data, ie. NulLReferenceException.
Just attach your debugger and set to to break when an exception is thrown.

Categories

Resources