Nested Partial View - c#

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.

Related

ASP.NET MVC Render Partial on Dropdown List Value Change

I'm trying to render a partial view dynamically when the user selects a value from the dropdown list. I've gotten quite far but can't figure out this last bit.
Here's the View/Quiz/AddQuestion.cshtml file
#model quizAppCodeFirst.ViewModels.QuestionTypes
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div>
Type #Html.DropDownList("QuestionTypes", ViewData["QuestionTypes"] as SelectList, new { #class = "form-control" })
<div id="partialPlaceHolder"> </div>
</div>
}
<script type="text/javascript" src="~/Scripts/jquery-3.3.1.min.js"></script>
<script type="Text/javascript">
$(function(){
$("#QuestionTypes").change(function(){
var stateId=$(this).val();
$("#partialPlaceHolder").load("#Url.Action("MyAction", "Quiz")/MCQ");
});
});
</script>
In the QuizController.cs, this is the MyAction method that renders the partials
public ActionResult MyAction(string id)
{
Type t = Type.GetType("quizAppCodeFirst.Quiz" + id);
return PartialView(viewName, t);
}
Till now, I've succeeded in rendering the partial view, but when I click the button inside the partial, it reloads the javascript / MyAction method.
Edit: To make it clear, I have main view which is View/Quiz/AddQuestions.cshtml and within it I am calling a partial view which is View/MCQ/AddMCQ.cshtml when the user selects MCQ from the dropdown list. The partial view (AddMCQ) loads perfectly fine and displays a form to add an MCQ with a Create button, but when I click the Create button, instead of calling the MCQController.cs/AddMCQ function, it calls the QuizController.cs/MyAction function. I can add the code for AddMCQ.cshtml but I feel like it's unnecessary since it works fine on its own.

POST Form which has a list of objects as a viewModel property (without ajax)

I have a view model which gets displayed in a cshtml view like this:
#model MyViewModel
<form action="...">
#foreach (var item in this.Model.MyList)
{
<input type="checkbox" name="item.Name"/>
}
</form>
Then I have a controller method on the backend:
[HttpPost]
public ActionResult SaveMyViewModel(MyViewModel viewModel)
{
...
When I inspect the viewModel in the controller method while POSTing, it has all null properties. I would expect it to have values in MyList and in there, bools for each item in MyList.
Is this possible without Ajax? I cannot use ajax here.
What must be done to the form in order to properly return an accurate representation of the viewModel back to the server?
I am assuming your button to submit the form is within the <form> element in HTML.
When you are POSTing a List (Array) of an Object from HTML to Controller, you need to add index of each item in the List to the HTML name property.
Additionally, the name property should also have same name as of the Property in the C# code.
Your code should look like below to work properly.
<form action="...">
#{
var i = 0;
}
#foreach (var item in this.Model.MyList)
{ var nameChecked = string.Empty;
if(item.Name != null) nameChecked = “checked”;
<input type="checkbox" name="viewModel[i].Name" #nameChecked />
i++;
}
</form>
NOTE: This is a basic code sample. You can always enhance to work more efficiently.
Hope this helps!

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?

RenderPartial with model from _Layout.cshtml

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";}

How to save partial view viewmodel items in main view viewmodel with Razor?

This might be a tricky question, but here it goes.
Let's say I have a main view and let's call it MainView.cshtml.
Now, MainView.cshtml has a dedicated ViewModel called MainViewModel.cs which holds a variable Model.ExampleItems which is an IEnumerable of class ExampleItem.
Now let's say I also have a partial view called _PartialView.cshtml whose ViewModel is just ExampleItem.
Okay so this is where I'm stuck. MainView.cshtml can dynamically call _PartialView.cshtml so that the user can create a new ExampleItem. How do I save each user-created ExampleItem to MainViewModel's IEnumerable of ExampleItems?
Here is some example code
MainView.cshtml
#model Models.ViewModels.MainViewModel
#foreach (var item in Model.ExampleItems)
{
await Html.RenderPartialAsync("_PartialView", item);
}
<button id="AddExampleItem" type="button" class="btn btn-primary">Add Example Item</button>
_PartialView.cshtml
#model Models.ExampleItem
<input asp-for="VariableOne" />
<input asp-for="VariableTwo" />
<input asp-for="VariableThree" />
How can I save ^^^ this into the original Model.ExampleItems?
You have two paths forward here. Either way, in order to bind the posted ExampleItems, the form fields must be named in the format ExampleItems[N].Property. So, to accomplish that, you can do:
Use a for loop, rather than a foreach and pass an HtmlFieldPrefix:
#foreach (var i = 0; i < Model.ExampleItems; i++)
{
var viewData = new ViewDataDictionary(ViewData);
viewData.TemplateInfo.HtmlFieldPrefix = "ExampleItems[" + i.ToString + "]";
await Html.RenderPartialAsync("_PartialView", Model.ExampleItems[i]);
}
You'll also need to make ExampleItems a List<ExampleItem> rather than IEnumerable<ExampleItem> or you'll raise an exception for enumerating twice (count and then for loop).
Create an editor template for ExampleItem and then utilize EditorFor. Just create a view at ~/Views/Shared/EditorTemplates/ExampleItem.cshtml, and put your partial code there, instead. Then in your main view you would just do:
#Html.EditorFor(m => m.ExampleItems)
Razor will intelligently determine that you passed it a collection and render the editor template for each item in the collection. Also, importantly, it will have the full context of the collection, so it will using proper name prefixes so that everything binds correctly on post.

Categories

Resources