Pass list of categories to partial view - asp.net-mvc

I have a partial view which will display list of Main Categories and under each Main Category all of its subcategories. But the problem is I don't know how can I pass this Category List to my partial view. Please check the code bellow. I've also attached my .edmx table map picture to give you better idea. Once I pass it to partial view I want to loop though all categories and sub categories to display them
[ChildActionOnly]
public PartialViewResult _GuestNav()
{
using (var db = new TestEntities())
{
db.Categories.ToList(); // get list from here
return PartialView("_GuestNav"); // then pass that list to partial view
}
}

Here is the main action code:
public ActionResult Categories()
{
using (var dbCtx = new DbContext())
{
var categories = dbCtx.Categories.Include(x => x.SubCategories).ToList()
return View(categories);
}
}
Then in your Categories.cshtml you will have the code as below:
#model IEnumerable<Categories>
<ul>
#foreach(var category in Model)
{
<li>#category.CategoryName
#if(category.SubCategories.Any())
{
Html.RenderPartial("~/Partial/_SubCategory.cshtml", category.SubCategories);
}
</li>
}
</ul>
At last you supply a partial view called _SubCategory.cshtml in the Partial folder of Category folder as below:
#model IEnumerable<SubCategory>
<ul>
#foreach(var subCategory in Model)
{
<li>#subCategory.SubCategoryName</li>
}
</ul>
In your case if you want to pass this list to the partial view you specified you can do it as below:
[ChildActionOnly]
public PartialViewResult _GuestNav()
{
using (var db = new TestEntities())
{
var categories = db.Categories.Include(x => x.SubCategories).ToList(); // Added the include if you want to add subcategories as well
return PartialView("_GuestNav", categories); // then pass that list to partial view
}
}

Yo can use model binding, pass a Model or ViewModel as a parameter and access it from the partial view. For example, in your _GuestNav action:
...
return PartialView("_GuestNav",db.Categories.ToList());
Here's a link on how to accomplish that.
Then you can bind the model in your view. For example:
...
#model IEnumerable<Categories>;
For more detail, check out the examples from the link.

The PartialView method has an override that accepts an object. You need to store the results of the db.Categories.ToList() call in a variable and pass that to the method like this:
using (var db = new TestEntities())
{
var cats = db.Categories.Include("SubCategories").ToList(); // get list from here
return PartialView("_GuestNav", cats); // then pass that list to partial view
}
Just make sure your partial view expects a list of categories as its model. Then, inside your view you can iterate over the model and display the subcategories.
You should also look into how to use a viewmodel for your views.
EDIT
You may need to use an include statement since navigation properties are generally lazy loaded. Updated my answer.

Related

pass viewModel object from controller to view in asp.net MVC

I have the following method in controller
public ActionResult Categorized()
{
var x=new Test.Models.MobileClass();
return View(x);
}
x object contains methods that return xmldocument data
how can I pass this object to view to access methods and display data from xmldocument in browser
I can display it element element by using the following code
document.writeln("#Model.getxml().ChildNodes.Count");
but I want to use for loop displaying the contents of object and the following code didn't work in javascript
var size=parseInt("#Model.Getxml().ChildNodes.Count");
for (var i=0; i<size; i++)
{
document.writeln("#Model.Getxml().ChildNodes[i].InnerText");
}
can you help me please
First of all, your view should not be calling Getxml and mucking about with the XML DOM. It's the job of the controller to present the view with "ready to render" data. That's what a ViewModel is for.
public ActionResult Categorized()
{
var foo = new Test.Models.MobileClass();
var xml = foo.Getxml();
var viewData = xml.ChildNodes.Cast<XmlNode>().Select(x => x.InnerText);
return View(viewData);
}
Now we're passing an IEnumerable<string> to the view, containing just the values we want to render.
In the view, you should not be using javascript to render your data to HTML - use the view to render your data to HTML - the Razor template engine is really good at that! Something like...
<ul>
#foreach (var item in Model) {
<li>#item</li>
}
</ul>
The javascript will fail, since the variable i will be evaluated on the server side and doesn't exist then.
Use a foreach in the view, with razor:
#foreach(var child in Model.Getxml().ChildNodes)
{
child.InnerText
}

How to populate mvc razor partial view

I need to create a view that displays Order Header information and I need to add a patial view that displays a grid of Line Items. The partial view will be strongly typed from a viewmodel. I understand that I should use the html helper #Html.Partial("Path/view"). I have only used controllers up til now to open a view, populating the viewmodel before sending it to the view. Since the partial view is being called form the html helper, I would like to know is what would be the best way to populate the parital view with the model data.
Option 1: Inherit from parent page
By default, any partial view rendered by calling #Html.Partial("PartialViewName") will get the view model passed to the parent view.
So if you have:
View Model
namespace MyNamesapce
{
public OrderInfoViewModel
{
public string OrderTitle { get; set; }
public IEnumerable<OrderItem> OrderItems { get; set; }
}
}
OrderInfo.cshtml
#model MyNamespace.OrderInfoViewModel
<h1>#Model.OrderTitle</h1>
#Html.Partial("OrderLineItems")
The OrderLineItems page should get a MyNamespace.OrderViewModel passed to it... so your partial view should look like this:
OrderLineItems.cshtml
#model MyNamespace.OrderInfoViewModel
foreach (var orderItem in Model.OrderItems)
{
//Do stuff
}
Option 2: Specify model
You can use the second parameter to specify the view model to be passed. I.e.
OrderInfo.cshtml
#model MyNamespace.OrderInfoViewModel
<h1>#Model.OrderTitle</h1>
#Html.Partial("OrderLineItems", Model.OrderItems)
OrderLineItems.cshtml
#model IEnumerable<OrderItem>
foreach (var orderItem in Model)
{
//Do stuff
}
Option 3: Use partial actions
If you need to reuse a partial view over multiple pages, it could be a good idea to use a partial view to eliminate having to populate different view models with the same info just because the page is going to be using the same partial.
E.g.
View Model
namespace MyNamesapce
{
public OrderInfoViewModel
{
public string OrderTitle { get; set; }
}
}
Controller
public class OrderController : Controller
{
public ActionResult OrderInfo(int orderId)
{
OrderInfoViewModel viewModel = GetViewModel(orderId);
return View(viewModel);
}
public PartialViewResult OrderLineItems(int orderId)
{
IEnumerable<OrderItem> orderItems = GetOrderItems(orderId);
return Partial(orderItems);
}
}
OrderInfo.cshtml
#model MyNamespace.OrderInfoViewModel
<h1>#Model.OrderTitle</h1>
#Html.Action("OrderLineItems")
OrderLineItems.cshtml
#model IEnumerable<OrderItem>
foreach (var orderItem in Model.OrderItems)
{
//Do stuff
}
With a partial view, you are just sending in a Model just like you would with a normal View. For example, if your Model has a property of LineItem objects named 'LineItems' you simply would do this:
#Html.Partial("_PartialName", Model.LineItems)
Now if your Model does not have that property, you can either add it, or pass it another way, like ViewBag (I prefer a strongly typed method, but that is my opnion:
#Html.Partial("_PartialName", (List<LineItem>)ViewBag.LineItems)
These are not the only ways, but they are my preferred methods.

MVC3 (Razor) Passing Model Data from View to Controller

I need to know if there is a way to pass the Model (or a part of it, i.e. thereafter a search query) data of a View (Razor Engine) to a Controller.
To explain in a better way what I have to do, that's the interested code:
VIEW:
#model IEnumerable<MvcMovie.Models.Movie>
#{
ViewBag.Title = "Index";
}
<h2>Index</h2>
...
#foreach (var item in Model) { ...}
...
#Html.ActionLink("Search", "SearchIndex")
#Html.ActionLink("Create Document", "createDoc"/*, new { id = Model.ToList() }*/)
I want to pass the
#model IEnumerable<MvcMovie.Models.Movie>
in the first line (= Model used in the foreach instruction) to the Controller "createDoc" to create a report document dynamically binded with the view.
I tried everything: I tried to use a ViewData (VIEW: ViewData["data"]=Model , CONTROLLER List movies= ViewData["data"]), I similarly tried a TempData, I tried to pass the Model as routeValues in the ActionLink (as you can see: new{ id= Model.toList() }), but nothing worked.
Is it even possible to do the thing I want to?
Can anyone help me?
Your model should not be IEnumerable<MvcMovie.Models.Movie> It should be a class, say SearchMovieModel, that has IEnumerable<MvcMovie.Models.Movie> Movies as one of its properties.
If you want a search model, something like this would be appropriate:
public class SearchMovieModel{
public IEnumerable<MvcMovie.Models.Movie> Movies {get;set;}
public string SearchString {get;set;}
}
you reference this model and its properties in your view and controller.
I guess I should add the method for parsing this in the controller.
On the first call to the view, the model does not exist. You need to create it in your controller:
public ActionResult Search(){
var model = new SearchMovieModel();
//you also need to instantiate the null objects unless you do that in the model's constructor
model.Movies = new List<Movie>();
return View(model);
}
To "reconvert" the POST data back to a model, you need to specify the model and method:
[HttpPost]
public ActionResult Search(SearchMovieModel model){
if (ModelState.IsValid){
//populate your IEnumerable<Movie> here.
return View(model);
}
// the complex collection will not be parsed back into the model. You will need to repopulate it.
model.Movies = new List<Movie>();
return View(model);
}
I think that know what you want... but whit this code
#Html.ActionLink("Create Document", "createDoc", new { id = Model.ToList() })
your html is..
Create Document
and that's because is render the type not the data
Solutions
define filter model to do the search again(the recommendation of jeremy-holovacs and mine)
why ask to the server the same data again? because if someone share that link... you can imagine whats it's the result even inject fake data that your app will generate
serialize data to json for example to forward it to the controller

Can you put a PartialView into a ViewData object?

I'm trying to mimic the webforms multiview functionality and the only way i can think of is to put a PartialView into a ViewData object? Something like the following:
View code:
<%= ViewData["PartialViewPlaceholder"] %>
Controller code:
if(//condition){
ViewData["PartialViewPlaceholder"] = partialView1;
} else {
ViewData["PartialViewPlaceholder"] = partialView2;
}
How would you go about this?
ViewData is meant to contain actual data, not views themselves, which contain markup and rendering code. Would it not be possible for you to do this:
public ActionResult MyActionMethod()
{
var model = new MyModel();
model.UsePartialView1 = false; // Tell the view not to use Partial View 1
return View("MyView", model);
}
And in the View MyView:
<% if (Model.UsePartialView1)
Html.RenderPartial("PartialView1", Model);
else
Html.RenderPartial("PartialView2", Model); %>
This will render either PartialView1 or PartialView2 using the same Model depending on the condition set by the Controller.
Or, to return a Partial View with a Model directly from your controller, instead of a normal View, you can do this:
public ActionResult MyActionMethod()
{
var model = ...
ViewData["MyViewData"] = ...
return PartialView("PartialView1", model);
}
This will return the Partial View PartialView1 directly to the client. This is mostly useful in AJAX scenarios, since the result will most probably not be an entire HTML page. Partial Views are .ascx files.

passing parameters to my partial view?

I am calling my partial view like this:
<% Html.RenderPartial("~/controls/users.ascx"); %>
Can I pass parameters to partial view? How will I access them in the actual users.ascx page?
You could pass a model object to the partial (for example a list of strings):
<% Html.RenderPartial("~/controls/users.ascx", new string[] { "foo", "bar" }); %>
Then you strongly type the partial and the Model property will be of the appropriate type:
<%# Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<System.Collections.Generic.IEnumerable<string>>" %>
<% foreach (var item in Model) { %>
<div><%= Html.Encode(item) %></div>
<% } %>
There is another overload for RenderPartial that will pass your model through.
<% Html.RenderPartial("~/controls/users.ascx", modelGoesHere); %>
How to access? Just like you normally would with any view:
<%= Model.MagicSauce %>
It took a while to sink in, but MVC means you use a Model, a View, and a Controller one way or another for just about everything, including Partial Views. How all three elements fit together can be a little intimidating at first. I'd never done one until just now, and it works --Woohoo!
Hope this helps the next person.... Sorry, I'm using razor instead of .Net forms. I'm also pulling data from a SQL Server database into Entity Framework, which a developer is likely to use. I also probably went a little overboard with WebGrid, which is so much more elegant than a foreach statement. A basic #webgrid.GetHtml() will display every column and row.
Background
In this working example, users have uploaded pictures. Their pictures are displayed in their edit form using a partial view. The ImageID and FileName metadata is persisted in SQL Server while the file itself is persisted in the ~/Content/UserPictures directory.
I know it's kinda half vast, because all the details of uploading and editing personal data isn't shown. Just the germane parts of using a Partial View are focused on, albeit with some bonus EF thrown in. The namespace is MVCApp3 for S&G.
Partial View Model ViewModels.cs
The SQL Server Images table includes many more columns in addition to ImageID and FileName such as [Caption], [Description], a MD5 hash to prevent the same image being uploaded multiple times, and upload date. The ViewModel distills the Entity down to the bare minimum needed for a user to see their pictures.
public class Picts
{
public int ImageID { get; set; }
public string FileName { get; set; }
}
Main View View Edit.cshtml
Note the cast/convert to strongly type the ViewData[].
#Html.Partial(
partialViewName: "Picts",
model: (IEnumerable<MVCApp3.Models.Picts>)ViewData["Picts"]
)
If you don't set the strongly-typed model to use for the Partial View you'll get a "The model item passed into the dictionary is of type 'System.Data.Entity.DynamicProxies..." error because it assumes you're passing the parent/master model.
Partial View View Picts.cshtml (the whole file contents is shown)
#model IEnumerable<MVCApp3.Models.Picts>
#{
var pictsgrid = new WebGrid(Model);
}
#pictsgrid.GetHtml(
tableStyle: "grid",
displayHeader: false,
alternatingRowStyle: "alt",
columns: pictsgrid.Columns(
pictsgrid.Column(format:#<text><img src="#Url.Content("~/Content/Users/" + #item.FileName)" alt="#item.ImageID" width="200" />
#Html.ActionLink(linkText: "Delete", actionName: "DeletePicture", routeValues: new { id = #item.ImageID })
</text>)
))
Controller IdentityController.cs
Set the data content into the ViewData["MyPartialViewModelKeyName"] your partial view will consume. You can give the dictionary key any name you want, but I gave it ViewData["Picts"] to be consistent with the partial view file name and its view model class definition.
Because the pictures may be shared amongst multiple users there is a many-to-many table with a corresponding PITA query in Entity Framework using nested froms and inner joins to return just the pictures belonging to, or shared with, a user:
public class IdentityController : Controller
{
private EzPL8Entities db = new EzPL8Entities();
// GET: /Identity/Edit/5
[Authorize]
public ActionResult Edit(int? id)
{
if (id == null)
return new HttpNotFoundResult("This doesn't exist");
// get main form data
ezpl8_UsersPhysicalIdentity ezIDobj = db.ezpl8_UsersPhysicalIdentity.Find(id)
// http://learnentityframework.com/LearnEntityFramework/tutorials/many-to-many-relationships-in-the-entity-data-model/
// get partial form data for just this user's pictures
ViewData["Picts"] = (from user in db.ezpl8_Users
from ui in user.ezpl8_Images
join image in db.ezpl8_Images
on ui.ImageID equals image.ImageID
where user.ezpl8_UserID == id
select new Picts
{
FileName = image.FileName,
ImageID = image.ImageID
}
).ToList();
return View(ezIDobj);
}
// Here's the Partial View Controller --not much to it!
public ViewResult Picts(int id)
{
return View(ViewData["Picts"]);
}
[Authorize] //you have to at least be logged on
public ActionResult DeletePicture(int id)
{
//ToDo: better security so a user can't delete another user's picture
// TempData["ezpl8_UserID"]
ezpl8_Images i = db.ezpl8_Images.Find(id);
if (i != null)
{
var path = System.IO.Path.Combine(Server.MapPath("~/Content/Users"), i.FileName);
System.IO.File.Delete(path: path);
db.ezpl8_Images.Remove(i);
db.SaveChanges();
}
return Redirect(Request.UrlReferrer.ToString());
}
protected override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
}
}
// get main form data
ezpl8_UsersPhysicalIdentity ezIDobj = db.ezpl8_UsersPhysicalIdentity.Find(id)
// http://learnentityframework.com/LearnEntityFramework/tutorials/many-to-many-relationships-in-the-entity-data-model/
// get partial form data for just this user's pictures
ViewData["Picts"] = (from user in db.ezpl8_Users
from ui in user.ezpl8_Images
join image in db.ezpl8_Images
on ui.ImageID equals image.ImageID
where user.ezpl8_UserID == id
select new Picts
{
FileName = image.FileName,
ImageID = image.ImageID
}
).ToList();
return View(ezIDobj);
}
// Here's the Partial View Controller --not much to it!
public ViewResult Picts(int id)
{
return View(ViewData["Picts"]);
}
[Authorize] //you have to at least be logged on
public ActionResult DeletePicture(int id)
{
//ToDo: better security so a user can't delete another user's picture
// TempData["ezpl8_UserID"]
ezpl8_Images i = db.ezpl8_Images.Find(id);
if (i != null)
{
var path = System.IO.Path.Combine(Server.MapPath("~/Content/Users"), i.FileName);
System.IO.File.Delete(path: path);
db.ezpl8_Images.Remove(i);
db.SaveChanges();
}
return Redirect(Request.UrlReferrer.ToString());
}
protected override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
}
}

Resources