RenderPartial with model from _Layout.cshtml - c#

One of the overloads for Html.RenderPartial allows you to pass in a model. How can you make use of that from _Layout.cshtml?
I have a model prepared with everything I need. I just can't see how to make it accessible to the Layout.
_Layout.cshtml
<!DOCTYPE html>
<html>
<head>
...
</head>
<body>
<nav>
#{ Html.RenderPartial("pMenu", menu); }
</nav>
...
pMenu.cshtml
The partial view for the menu. Hopefully this makes it clear that the menu is dynamic and really does need a model passed to it.
#model MyApplication.Menu
#foreach (KeyValuePair<string, MyApplication.MenuGroup> group in Model.group)
{
if (group.Key != "default")
{
<div>#group.Key.ToString()</div>
}
<ul>
#foreach (MyApplication.MenuItem item in group.Value.items.Values)
{
<li>
#if (item.isCurrent)
{
<span class="#item.Icon">#item.LinkText</span>
}
else
{
#Html.ActionLink(item.LinkText, item.Action, item.Controller, item.RouteValues, new { #class = #item.Icon })
}
#if (null != item.subItems)
{
<ul class="tree">
#foreach (MyApplication.SubItem subitem in item.subItems)
{
<li>
#if (subitem.isCurrent)
{
<span>#subitem.LinkText</span>
}
else
{
#Html.ActionLink(subitem.LinkText, subitem.Action, subitem.Controller, subitem.RouteValues, null)
}
</li>
}
</ul>
}
</li>
}
</ul>
}
Where is the menu instantiated?
public class MainController : Controller
{
protected override void Initialize(System.Web.Routing.RequestContext requestContext)
{
base.Initialize(requestContext);
menu = new Menu(requestContext.RouteData.Values);
Background information
This is an intranet application. It does not have the typical hard-coded menu options of "Home", "About", "Contact". Most of the menu is created from user-maintained database records. The menu is individualised to the user. No two users will have the same menu items. Eg: A manager gets a menu item for each of their staff.
The menu model also handles other factors that make the menu "dynamic" such as:
identifying whether the requested page matches a given menu item (by route data) (for styling)
adding extra menu items based on roles
sometimes adding sub-menus for the current context
This seems similar to #Render partial with a model in Layout View but I think that question went off the rails by referring to session.
Disclaimer: I'm an experienced programmer but relatively new to C# and asp.NET MVC.

ViewData to the rescue.
_Layout.cshtml
#{ Html.RenderPartial("pMenu", ViewData["menu"]); }
MainController
menu = new Menu(requestContext.RouteData.Values);
ViewData["menu"] = menu;
Modifying menu after this point still works. I'm not sure why, but I'll take it.
See also Best way to do global viewdata in an area of my ASP.NET MVC site?

I think you want to render a menu. from my view you should create a helper method for this purpose. If you want to go through this technique and if you are new to create your own tag then following this basic tutorial to get some undestanding : http://www.codeproject.com/Articles/787320/An-Absolute-Beginners-Tutorial-on-HTML-Helpers-and

You could always include a base index view that contains generic things then reference your layout in the page. You somewhat inherit the view and send in model data.
#model Model.MyIndexModel
#{Layout = "~/Views/Shared/Layouts/_SinglePageLayout.cshtml";}

Related

Nested Partial View

On our website the client can add different items into the footer columns, ie a text box. We wanted to be able to keep the code for these different items in one partial view, as the items are used in other areas of the site and want to keep code duplication to a minimum.
I was wondering if it possible to nest a Partial View within a foreach statement and then pass an if statement based on the parent foreach.
I have the following code in a partial view called Three_Column_Footer:
#inherits Umbraco.Web.Mvc.UmbracoTemplatePage
#{
var root = Model.Content.Site();
var thefooter = root.FirstChild("footer").Children();
}
<footer class="footer color-main">
<div class="container">
<div class="row">
#foreach(var foot in thefooter){
var childcol = foot.Children;
<div class="col-6">
#foreach(var item in childcol){
#Html.Partial("Footers/FooterStubs", item)
}
</div>
}
</div>
</div>
</footer>
I need someway of getting the above foreach to be recognised in the Partial called FooterStubs. Can this be done with an #inherits Umbraco.Web.Mvc.UmbracoViewPage<IPublishedContent> or a Model.GetPropertyValue<IEnumerable<IPublishedContent>> - if so how?
Thanks!
The Partial method has an overload that accepts additionalViewData. You can utilize this to provide any additional context you need the partial to have:
#Html.Partial("Footers/FooterStubs", item, new ViewDataDictionary(ViewData)
{
{ "foot": foot }
});
Then in your partial:
#{
var foot = ViewData["foot"] as YourFootClass;
}
I'm not sure what info you actually need in the partial, but you can essentially add whatever you like in this way. Just bear in mind that ViewData is a dynamic dictionary, so you'll have to type whatever you get out of it.

mvc paging inside an html form

I'm trying to create paging inside html form.
How do I make each page button change the model's CurrentPage property?
I'm new to MVC, so bear with me please.
I have a list of reports that I need to display in a view named Search. I made a model for that (ReportViewModel), and another model that holds a list of those, named SearchViewModel. In SearchViewModel I have some other variables like SearchName, SearchDate, CurrentPage, PageCount, PageSize, etc. The goal is to do paging and searching in one action named Search in ReportsController.
Now, I would like to keep that action simple by giving that function only SearchViewModel instance as a parameter, so that inside I use all variables of that model. Doing that, I won't have to type parameter names twice (once in SearchViewModel and once as parameters in Search action). I will not need that search function elsewhere, so it's ok for it to not have any parameters other than SearchViewModel instance.
So, very loosely, it looks something like this:
ReportsController.cs
public class ReportsController : Controller
{
public ActionResult Search(SearchViewModel model)
{
if (!ModelState.IsValid)
return View(model);
// do search magic here
return View(model)
}
}
Search.cshtml
#model ISFinReports.Models.SearchViewModel
#{
ViewBag.Title = "List";
Layout = "~/Views/_masterLayout.cshtml";
}
<link href="~/CSS/Search.css" rel="stylesheet" />
<script src="~/Scripts/Search.js"></script>
#using (Html.BeginForm("Search", "Reports", FormMethod.Get))
{
<div id="divSearchBar">
// add #Html.DropDownListFor, #Html.TextBoxFor and submit button here
</div>
<div id="divReportsTable">
<table id="tblReports">
#if (Model.Reports != null)
{
foreach (var r in Model.Reports)
{
// add tr, td of all reports in report list
}
}
</table>
</div>
<div style="text-align:center">
#for (int i = 0; i < Model.PageCount; i++)
{
#Html.ActionLink(i.ToString(), "Search", "Reports", new { CurrentPage = i });
// or
// <input type="submit" value="#i" onClick="meybe something like this?" />
}
Current Page #Html.EditorFor(q => q.CurrentPage), of total #Model.PageCount pages
</div>
}
I know that I can simply type twice all the data I need to flow between controller and view, once in model and once as Search action parameters, but that's extactly what I'm trying not to do. Currently I have a textbox for editing the current page, and it works, because it's created via #Html.EditorFor, but I'd like to have multiple buttons for each page.
I could maybe create a hidden field in that form, and create a javascript function that'll find that hidden field by id, and change it's value, and then call whatever the submit button onClick function is. BUT, is there a better/shorter/more-MVC way to implement that?

Rendering a partial view in MVC with Javascript

I've got an asp.net MVC website that consists of a topbar and a main area.
Via a js function, I want to be able to retrieve new data from my Controller and display these data in the main area of my website, but without re-rendering the topbar area.
How do I do this?
Here is what I got:
I put the topbar and all related scripts in the _layout.cshtml, scripts related to the mainview go to Display.cshtml and the data itself which I want to display go inside Partial_ChartView.cshtml
The partial view that should display my chart data is loaded inside Display.cshtml like this:
#model MobileReports.Models.ReportViewModel
#{
ViewBag.Title = "Display";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#{Html.RenderPartial("Partial_ChartView");}
The partial view Partial:ChartView.cshtml looks like this:
#model MobileReports.Models.ReportViewModel
<div id="chartContainer">#Model.Chart</div>
The corresponding controller contains this code:
public ActionResult Display(Guid? id)
{
ReportViewModel viewModel = new ReportViewModel();
Guid validId = (Guid)id;
viewModel.Chart = GetChart(guid);
viewModel.ChartData = GetData(guid);
return PartialView(viewModel);
}
When I open the page at ../Report/Display , the page seems to get rendered correctly.
Now I want to add a script that calls Display(id) with a certain value. Then I want to re-render only the main area (inside div #chartContainer) to display the new data which should now be in the model.
How do I do this?
Create a new method that returns a partial view containing only the data you need
public ActionResult Chart(GUID id)
{
.....
return PartialView(someModel);
}
then use jquery .load to replace the contents of your div
$('#chartContainer').load('#Url.Action("Chart", "Report")', { id: YourGUIDValue });

MVC 5 show Partial-View in Layout only on Home Index

I use a sidebar on my website with stats from my database and statics data like links and other texts.
In my _Layout.cshtml, I use Html.RenderAction("SidebarPV", "Home"); to call the sidebar.
The sidebar is a Partial-View using a ViewModel for the stats.
SidebarPV is generated in my HomeController like that :
public ActionResult SidebarPV() {
SidebarViewModel viewmodel = new SidebarViewModel();
DateTime now = DateTime.Now;
viewmodel.stat_data1 = db.Table1.Where(e => e.DateDeb <= now && e.DateFin >= now).Count();
viewmodel.stat_data2 = db.Table2.Where(c => c.DateDeb <= now && c.DateFin >= now).Count();
return PartialView("SidebarPV", viewmodel);
}
It works like a charm but I don't need stats on all views, only on /Home/Index
So I want to 'comment' the stats generation when the ser is not on the index of the website.
Thanks for advices.
EDIT (solution, thanks to krillgar) :
I wrote in my _Layout
#{
var isHome = ViewContext.RouteData.Values["controller"].ToString().ToUpper() == "HOME";
var isIndex = ViewContext.RouteData.Values["action"].ToString().ToUpper() == "INDEX";
if (isHome && isIndex) {
Html.RenderAction("SidebarPV", "Home");
}
else {
Html.RenderAction("SidebarNoStatPV", "Home");
}
}
I know I need to create two partial-views but one is static so I will not edit it for a long time :).
Tested, It works.
There's also another solution. As for me, i hate things like this
ViewContext.RouteData.Values["controller"].ToString().ToUpper() == "HOME"
It has to work with magic strings, which is not even a const, just runtime string. It may provide some issues in advance. And what if you will need it on some another page?
I'd recommend you to use nested layouts. You can create _Layout with section
UPDATE:
_Layout.cshtml:
<...>
#Html.RenderSection("sidebar", false)
<...>
And then on your home page you may just use _SidebarLayout instead of _Layout. And whenever you will need sidebar on any page, you can do just the same.
So your home page will look like
#{
Layout = "~/Views/_Layout.cshtml";
}
#section sidebar {
#Html.RenderAction("SidebarPV", "Home")
}
And all other pages will look like
#{
Layout = "~/Views/_Layout.cshtml";
}
#section sidebar {
#Html.RenderAction("SidebarNoStatPV", "Home")
}
If you don't want to repeat yourself with this "SidebarNoStatPV" you can use nested layouts:
_NoStatLayout.cshtml
#{
Layout = "~/Views/_Layout.cshtml";
}
#section sidebar {
#Html.RenderAction("SidebarNoStatPV", "Home")
}
and use it as the layout for any page but Home. If you will need to extend sidebar with additional info for different pages you can just put #Html.RenderSection("sidebar", false) inside sidebar section in _NoStatLayout.cshtml.
Why do i consider it as a better option? It fits the SRP, for only Home page should be responsible for it's own unique data.
If you want to keep the code to call the action in the _Layout page, then you just need to get information of what action is being called when generating the page. Add the following to the top of your _Layout:
var isHome = ViewContext.RouteData.Values["controller"].ToString().ToUpper() == "HOME";
var isIndex = ViewContext.RouteData.Values["action"].ToString().ToUpper() == "INDEX";
Then wrap your call to generate the partial view wherever you need it in _Layout with the following:
if (isHome && isIndex) {
Html.RenderAction("SidebarPV", "Home");
}

Implement back button in ASP.NET MVC

I am facing issue implementing a back button feature in a ASP.NET MVC application.
This is my Home page View, for simplicity let us consider it has a div which has img element and div that renders PartialView2. PartialView2 is a partial view which generates list of tiles based on the data from the Model.
View1:
<div> remainig page content </div>
<div>
<img src="/path/to/img_back" id="img" />
<div id="1">
Html.RenderAction("PartialView2", "Home");
</div>
</div>
<div> remainig page content </div>
PartialView2:
#model ICollection<Model>
#{
foreach(item in Model) {
<div class="tile">
<!-- tiles data generated using model data that I get from controller -->
</div>
}
}
HomeController:
//field
private Stack<ICollection<Model>> myStack = new Stack<ICollection<Model>>();
public PartialViewResult GetTiles(string id) {
var nodes = new List<Model>();
//make a call to database and generating the list of child nodes of type Model.
// get the rows from the db which have parentid as the passed value.
//for back button: myStack.push(nodes);
return PartialView("PartialView2",nodes);
}
//Method to call from jQuery on click of #img
public PartialViewResult BackButton() {
return PartialView("PartialView2",myStack.Pop());
}
When I click on a tile, I make a call to GetTiles method in the home controller through Jquery Ajax and populate the $("#1").html() with the result returned from the ajax call which is the same partial view.
When I click on #img I call BackButton() method from jQuery ajax to to load html of #1 div in the page.
Model:
string icon;
string title;
string parentid;
string id;
bool hasChildren;
With this design I only have the options to traverse the children and represent them as tiles but I need to implement a back button so as to traverse the parents. I have created a stack in the home controller and pushed the elements to the stack for each call to the GetTiles() method. But for every call to the controller, a new stack is instantiated. So my idea to have collection of models in the stack and on each call to back button pop the collection of Model and return it to the view is not working.
Can you guys please help me solve this issue? Any in-memory data structures to use to hold the parent models?
Please let me know if I need to provide further information.

Categories

Resources