Partial View different model - asp.net-mvc

I am trying to use a partial view that uses a different model than the one used in the main view. The partial view has
to show a list with the products recently added. But I am stuck on how and where to implement the logic for retrieving the data I need from the database.
Home/Index.cshtml:
#Html.Partial("~/Views/Shared/_LatestProducts.cshtml", new List<Website.Models.LatestProductsList>())
Shared/_LatestProducts.cshtml:
#model List<Website.Models.LatestProductsList>
#foreach (var item in Model)
{
<a href="#" title="img">
<img src="~/Content/images/latest-product-img.jpg" alt="" /><p>#item.ProductName</p>
</a>
}
And I have the following code that I am trying to use in order to get some products for tests and show them in the partial view:
public PartialViewResult _LatestProducts()
{
List<LatestProductsList> latestProd = (from p in db.Products
where p.ID < 5
select new LatestProductsList { ProductName = p.Title }).ToList();
return PartialView(latestProd);
}
I thought that I might use it in the HomeController, but that obviously doesn't work and I am not sure if partial views should have their own controller, if I can
just call it from another class. I am still wrapping my head around ASP MVC, so any help will be appreciate it.

Just call the action that renders the partial view in Index.cshtml.
#Html.Action("_LatestProducts", "Product")
Second parameter is the name of the controller that has the _LatestProducts method.
Just a reminder: Names with _ prefix is for partial views only, not action methods. You should rename it to LatestProducts.

Related

ChildActionOnly attribute not working [duplicate]

When would you use the attribute ChildActionOnly? What is a ChildAction and in what circumstance would you want restrict an action using this attribute?
The ChildActionOnly attribute ensures that an action method can be called only as a child method
from within a view. An action method doesn’t need to have this attribute to be used as a child action, but
we tend to use this attribute to prevent the action methods from being invoked as a result of a user
request.
Having defined an action method, we need to create what will be rendered when the action is
invoked. Child actions are typically associated with partial views, although this is not compulsory.
[ChildActionOnly] allowing restricted access via code in View
State Information implementation for specific page URL.
Example: Payment Page URL (paying only once)
razor syntax allows to call specific actions conditional
With [ChildActionOnly] attribute annotated, an action method can be called only as a child method from within a view. Here is an example for [ChildActionOnly]..
there are two action methods: Index() and MyDateTime() and corresponding Views: Index.cshtml and MyDateTime.cshtml.
this is HomeController.cs
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "This is from Index()";
var model = DateTime.Now;
return View(model);
}
[ChildActionOnly]
public PartialViewResult MyDateTime()
{
ViewBag.Message = "This is from MyDateTime()";
var model = DateTime.Now;
return PartialView(model);
}
}
Here is the view for Index.cshtml.
#model DateTime
#{
ViewBag.Title = "Index";
}
<h2>
Index</h2>
<div>
This is the index view for Home : #Model.ToLongTimeString()
</div>
<div>
#Html.Action("MyDateTime") // Calling the partial view: MyDateTime().
</div>
<div>
#ViewBag.Message
</div>
Here is MyDateTime.cshtml partial view.
#model DateTime
<p>
This is the child action result: #Model.ToLongTimeString()
<br />
#ViewBag.Message
</p>
if you run the application and do this request http://localhost:57803/home/mydatetime
The result will be Server Error like so:
This means you can not directly call the partial view. but it can be called via Index() view as in the Index.cshtml
#Html.Action("MyDateTime") // Calling the partial view: MyDateTime().
If you remove [ChildActionOnly] and do the same request http://localhost:57803/home/mydatetime it allows you to get the mydatetime partial view result:
This is the child action result. 12:53:31 PM
This is from MyDateTime()
You would use it if you are using RenderAction in any of your views, usually to render a partial view.
The reason for marking it with [ChildActionOnly] is that you need the controller method to be public so you can call it with RenderAction but you don't want someone to be able to navigate to a URL (e.g. /Controller/SomeChildAction) and see the results of that action directly.
FYI, [ChildActionOnly] is not available in ASP.NET MVC Core.
see some info here
A little late to the party, but...
The other answers do a good job of explaining what effect the [ChildActionOnly] attribute has. However, in most examples, I kept asking myself why I'd create a new action method just to render a partial view, within another view, when you could simply render #Html.Partial("_MyParialView") directly in the view. It seemed like an unnecessary layer. However, as I investigated, I found that one benefit is that the child action can create a different model and pass that to the partial view. The model needed for the partial might not be available in the model of the view in which the partial view is being rendered. Instead of modifying the model structure to get the necessary objects/properties there just to render the partial view, you can call the child action and have the action method take care of creating the model needed for the partial view.
This can come in handy, for example, in _Layout.cshtml. If you have a few properties common to all pages, one way to accomplish this is use a base view model and have all other view models inherit from it. Then, the _Layout can use the base view model and the common properties. The downside (which is subjective) is that all view models must inherit from the base view model to guarantee that those common properties are always available. The alternative is to render #Html.Action in those common places. The action method would create a separate model needed for the partial view common to all pages, which would not impact the model for the "main" view. In this alternative, the _Layout page need not have a model. It follows that all other view models need not inherit from any base view model.
I'm sure there are other reasons to use the [ChildActionOnly] attribute, but this seems like a good one to me, so I thought I'd share.
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.TempValue = "Index Action called at HomeController";
return View();
}
[ChildActionOnly]
public ActionResult ChildAction(string param)
{
ViewBag.Message = "Child Action called. " + param;
return View();
}
}
The code is initially invoking an Index action that in turn returns two Index views and at the View level it calls the ChildAction named “ChildAction”.
#{
ViewBag.Title = "Index";
}
<h2>
Index
</h2>
<!DOCTYPE html>
<html>
<head>
<title>Error</title>
</head>
<body>
<ul>
<li>
#ViewBag.TempValue
</li>
<li>#ViewBag.OnExceptionError</li>
#*<li>#{Html.RenderAction("ChildAction", new { param = "first" });}</li>#**#
#Html.Action("ChildAction", "Home", new { param = "first" })
</ul>
</body>
</html>
Copy and paste the code to see the result .thanks

Partial Views, redirected to another URL?

Am trying to build a project using MVC 4 and Razor. Am having trouble understand partial views. I have a list of my domain objects that I iterate through and display it in a list box (each row being clickable). I split this into main view and a partial view which renders the domain object which works fine. Inside my partial view I want to make each item clickable and upon click, I want to create a new partial view displaying details about the domain object.
Here is what I have
My main view looks like this
<div class="panely">
<div class="list-group">
#{ Html.RenderPartial("DomainObjectsPartial");}
</div>
</div>
My partial view looks like this
<div class="list">
#foreach (var x in #Model)
{
<a href="#Html.Partial("DomainObjectPartial")" class="list-item">
<em>#x.Name</em>
</a>
}
</div>
I have a view named DomainObjectPartial, which has nothing but a small div with hello.
When the user clicks on a domain object, I expect the partial view to be rendered within the main view, but instead I get a error saying
A potentially dangerous Request.Path value was detected from the
client (<).
And when I look at my URL, the contents of the partial view are contained within it like
http://localhost/<div>hello</div>
I dont want to be redirected to another URL. I just want the partial view to be displayed below the list. Can anyone explain to me what am i missing or not understanding?
I guess you wanted to use AJAX:
<div class="list">
#foreach (var x in Model)
{
<a href="#Url.Action("Index", "Items", new { id = x.Id })" class="ajax-link">
<em>#x.Name</em>
</a>
}
</div>
and then you will obviously have a controller action which will render this partial:
public class ItemsController: Controller
{
public ActionResult Index(string id)
{
// go get the specific item from your database using the id and pass it to
// the partial view
var viewModel = ...
return Partialview("DomainObjectPartial", viewModel);
}
}
and the last part is to AJAXify this anchor:
$(function() {
$('.ajax-link').on('click', function() {
// Send an AJAX call to the server endpoint pointed by the href attribute
// of the anchor and inject the results of this endpoint execution inside a
// DOM element with id="result"
$('#result').load(this.href);
// By returning false you are canceling the default action of the
// anchor click and prevent the browser to redirect to the url pointed
// by the href property. This would leave enough time for your AJAX request
// to execute and return the results.
return false;
});
});
and you will obviously need a DOM element with id="result" somewhere on your page to harbor the results of the AJAX call:
<div id="result"></div>

Partial View submit returns null

I'm trying to create a single view which allows the user to see the currently listed items using the index view model and then also allows the user to create a new item using a seperate create view model
So I've got two viewModels
-IndexFunkyThingsViewModel
-CreateFunkyThingViewModel
In essence I've got a main view:
#model IndexFunkyThingsViewModel
#foreach (var item in Model.FunkyThings)
{
/*Indexy stuff*/
}
/*If create item*/
#if (Model.CreateFunkyThing)
{
#Html.Partial("_CreateFunkyThingPartial", new CreateFunkyThingViewModel());
}
Then in my partial view I have
#model CreateFunkyThingViewModel
#using (Html.BeginForm(MVC.FunkyThings.CreateFunkyThing(Model)))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Create FunkyThing</legend>
#Html.EditorForModel();
<p>
<input type="submit" class="button green" value="CreateFunkyThing" />
</p>
</fieldset>
}
Finally in the controller I have:
[HttpPost]
public virtual ActionResult CreateFunkyThing(CreateFunkyThingViewModel createFunkyThingViewModel)
{
..
}
This all seems to compile happily and when I go to the view it works so far as displaying the create fields and such like. However when I then hit the submit button the controller receives no data. The ActionResult is called however in the debugger the createFunkyThingViewModel parameter is null when called by the submit button.
What am I doing wrong?
When posting to your controller, you're not sending the model down to it. Use this:
#using (Html.BeginForm("CreateFunkyThing", "ControllerName", FormMethod.Post))
then remove the p tags from around the button, and don't use anything with it.
The paragraph tags have a tendency to group the button separately from a form even if they are in the same outer container.

Not able to return a view for [HttpPost] method

I get the following error when returning a view:
Server Error in '/' Application.
--------------------------------------------------------------------------------
The view 'student' or its master was not found. The following locations were searched:
~/Views/Student/student.aspx
~/Views/Student/student.ascx
~/Views/Shared/student.aspx
~/Views/Shared/student.ascx
Here is my controller action:
[HttpPost]
public ActionResult SubmitStudent()
{
StudentViewModel model = TempData["model"] as StudentResponseViewModel;
ViewData["id"] = model.Id;
ViewData["name"] = model.Name;
string comment = Request["comment"];
var student = student.studentTable.Where(s => s.studentId == model.Id);
return View(student);
}
Here is my View:
<%# Page Language="C#" Inherits="System.Web.Mvc.ViewPage<IEnumerable<string>>" %>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
<title>Student</title>
</head>
<body>
<div>
Student name listed below:
</div>
<table>
<% foreach (var item in Model) { %>
<tr>
<td>
<%= Html.Encode(item)%>
</td>
</tr>
<% } %>
</table>
</body>
</html>
A few things to consider here.
First of all, returning a view after a HTTP POST is really a bad design choiche. You can google about the PRG Pattern and you will find many articles that will explain why you should always redirect to a HTTP GET which will render your view.
Second, I find strange that your code is looking for a view name "student". As per MVC specification, the controller will look for a view named as the action method unless an overload of the View() method which accepts the view name as parameter is called (which is not your case, at least not in the code you posted).
In your example, it seems like it should look for a view named "SubmitStudent". Again, the model type you declare on your view doesn't match the model you're passing to it. It accepts an IEnumerable<string> but you're passing to it an IQueryable<Student> (that's what your student variable contains).
I think you omitted some parts of your code. The parts you posted don't quite match with one another.
In order for your code to work, you're going to need a view called SubmitStudent.aspx inside the Views\Student\ or Views\Shared\ folders.
It also looks odd that your view inherits a list of strings and not a Student object or whatever type of object your query returns. Your view is expecting an enumerable list of string's
This line is also confusing:
var student = student.studentTable.Where(s => s.studentId == model.Id);
Did you mean:
var student = model.studentTable.Where(s => s.studentId == model.Id);
Your view must be in "Views\Student\" - unless you have changed the view engine settings which I imagine you have not.
So I believe your view is not there.

asp.net mvc 3 Model with complex property and checkboxes

I have a typed view as Item (one class that i created) with a form inside to add Items to my database. This Item class has one property called Categories that is a List (Category has 2 properties ID and Name)
Im using an editorfor in my view:
<div>
#(Html.EditorFor(e => e.Categories, "Categories"))
</div>
I created an EditorTemplatefor called "Categories.cshtml" render all the available categories:
#{
Layout = null;
}
#model List<Category>
#{
foreach (Category category in ((BaseController)this.ViewContext.Controller).BaseStateManager.AvailableCategories)
{
#Html.Label("test", category.Name)
<input type="checkbox" name="Categories" value="#(category.ID)" />
}
}
The checkboxes are well rendered (one for every Available category in cache), but after clicking in some, and post the form, im receiving my instance of Item but with the property Categories empty.
What i have to do to receive my List Categories completely instantiated after submit the form?
Dont loop it. Let the framework generate the code for you (then, it will know how to build it back and bind it to your controller).
Just pass the list to the editor template and mvc will do the rest. Check my blog post on something similar.
Try using an index based loop. This ensures MVC will render the item's attributes in such a way that allows the default model binder to instantiate the model on post back. Also, use the Html helper for the checkbox as well:
var categories = ((BaseController)this.ViewContext.Controller).BaseStateManager.AvailableCategories;
for (var index = 0; index < categories.Count; index ++)
{
#Html.Label("test", categories[index].Name)
#Html.Checkbox("ID", categories[index].ID)
}

Resources