ASP.NET MVC rendering sections for dynamically driven content (Widgets) - c#

I am receiving the following error in ASP.NET MVC. "the following sections have been defined but have not been rendered for the layout page" - Now I know what the error is about, but can't figure a way around it in fact it probably should work but I don't know why it won't.
I have an index.cshtml page which renders all dynamically driven DB content. Pages can contain widgets (Reusable blocks on content). The index.cshtml code is below:
#{
Layout = AppSettings.LayoutFileDirectory + "/" + PageDAL.GetLayoutFileForPage(Model.Page);
string currentSection = string.Empty;
}
#if(!string.IsNullOrEmpty(Model.Page.PageTitle))
{
<h3>#Model.Page.PageTitle</h3>
}
#Html.Raw(Model.Page.PageText)
#foreach (var item in Model.Page.WidgetsInPages)
{
//if(IsSectionDefined(item.WidgetSection))
//{
string sectionName = item.WidgetSection;
//don't want to redefine section
if (currentSection!= sectionName)
{
DefineSection(sectionName, () =>
{
//render all this sections widgets
foreach (var insider in Model.Page.WidgetsInPages.Where(w => w.WidgetSection == sectionName).OrderBy(w => w.WidgetSortOrder))
{
if (insider.Widget.WidgetFile != null)
{
Write(Html.Partial(AppSettings.WidgetTemplatesDirectory + "/" + insider.Widget.WidgetFile, item.Widget));
//Write(Html.Partial(AppSettings.WidgetTemplatesDirectory + "/" + insider.Widget.WidgetFile, new {Widget=item.Widget}));
}
}
});
currentSection = sectionName;
}
//}
}
Now I have a _DefaultTheme.cshtml with the following sections. (This is the primary layout page)
<div class="header">
#RenderSection("_HeaderSection", false)
</div>
<div class="container">
#RenderSection("_AboveBodySection", false)
#RenderBody()
#RenderSection("_BelowBodySection", false)
</div>
#RenderSection("_AfterBodySection", false)
<footer class="footer">
<div class="container">
#RenderSection("_FooterSection",false)
#Html.Raw(FrontEnd.PopulateFooter(Model.Website))
</div>
</footer>
Adding widgets to the any page which has the _DefaultTheme layout works perfectly fine, but when I start inheriting pages it becomes an issue. I now have another page _DefaultThemeArticles.cshtml the master page is _DefaultTheme.cshtml
The _DefaultThemeArticles.cshtml contains this:
#{
Layout = "~/Themes/_DefaultTheme.cshtml";
}
<div class="container">
#RenderBody()
</div>
Now the error occurs when adding Widgets to any page which has this layout. I get this error:
The following sections have been defined but have not been rendered for the layout page "~/Themes/_DefaultThemeArticles.cshtml": "_BelowBodySection".
But the section _BelowBodySection has been defined in the master page, why would it not work here?.

The problem is that in your layout page, _DefaultTheme.cshtml, by writing all of these:
#RenderSection("_HeaderSection", false)
#RenderSection("_AboveBodySection", false)
#RenderSection("_BelowBodySection", false)
#RenderSection("_AfterBodySection", false)
#RenderSection("_FooterSection",false)
You are asserting that in any child page, like _DefaultThemeArticles, there must be a section defined for each of the above. If Razor doesn't find a particular section defined (in your case _BelowBodySection) in the child page, it throws the error. You can use the following Razor to solve this:
#if (IsSectionDefined("_BelowBodySection"))
{
#RenderSection("_BelowBodySection")
}
This will only render the section if it exists.
Hope this helps.

Related

In ASP.net, what kind of IF statement could I use to hide a div if the image inside it matches the current page URL?

This is within Sitefinity if that matters, and I am really new at ASP.NET and C#.
I have an image-based navigation element at the bottom of a page that links to different articles using the same template. There are 5 articles, and I would like the link to the active page/article to be hidden so there is a grid of 4 image links.
Here's a screenshot:
https://i.imgur.com/PG2Sfpo.png
Here is the code behind it:
#{
string navTitle = string.Empty;
string url = string.Empty;
if (Model.CurrentSiteMapNode != null && Model.CurrentSiteMapNode.ParentNode != null)
{
if (Model.CurrentSiteMapNode.Title == "Home")
{
navTitle = Model.CurrentSiteMapNode.ParentNode.Title;
}
else
{
navTitle = Model.CurrentSiteMapNode.Title;
}
url = Model.CurrentSiteMapNode.ParentNode.Url;
}
}
<div class="foundation-stories-container">
#foreach (var node in Model.Nodes)
{
#RenderRootLevelNode(node);
}
</div>
#*Here is specified the rendering for the root level*#
#helper RenderRootLevelNode(NodeViewModel node)
{
string[] thisPage = (node.Url).Split('/');
string thisImage = thisPage[4] + ".jpg";
<a href="#node.Url" target="#node.LinkTarget">
<div class="foundation-story-block">
<div class="hovereffect">
<img src="[OUR WEBSITE URL]/stories/#thisImage" class="img-fluid">
<div class="overlay">
<h2>#node.Title</h2>
</div>
</div>
</div>
</a>
}
So we're already getting the page URL and image file name
string[] thisPage = (node.Url).Split('/');
string thisImage = thisPage[4] + ".jpg";
Is this as easy as doing the following?
if (thisImage = thisPage)
{
foundation-story-block.AddClassToHtmlControl("hide")
}
Seems easy enough, but I don't know where to start.
I'm better at Javascript, so I do have a JS solution in place for this already, but I'd really like to find a cleaner way to do it.
<script type="text/javascript">
$(document).ready(function() {
var active = window.location.pathname.split("/").pop()
var name = active;
name = name.replace(/-/g, ' ');
jQuery.expr[":"].Contains = jQuery.expr.createPseudo(function(arg) {
return function( elem ) {
return jQuery(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >=
0;
};
});
$("h2:Contains('" + name + "')").closest(".foundation-story-block").addClass("hide");
});
</script>
This exists on the main template page.
Gets the last part of the URL
Sets that as a variable called "name"
Changes the dash to a space if there is one (most of the pages are associated with names so it's like /first-last)
Then it goes and looks at the which is where the title of the page lives, and if it equals the "name" variable, the ".hide" class is added to the block.
Thanks for any help anyone can provide.
You could bind a click event to your elements with the foundation-story-block class. The reason I use .on instead of .click is because when using UpdatePanels the click event won't fire after an UpdatePanel has it's update event triggered - you might encounter a similar problem with your dynamic binding so I used .on to avoid this.
$(".foundation-story-block").on("click", function() {
// Remove the "hide" class from any elements that have it applied
$.each($(".foundation-story-block.hide"), function(index, value) {
// Remove the class using the "this" context from the anonymous function
$(this).removeClass("hide");
});
// Add the "hide" class to the element that was clicked
$(this).addClass("hide");
});
I haven't run this though an IDE so it might not be 100% correct but it will put you on the correct path.
It is possible, yes. Here is how:
...
#{
var hiddenClass = thisImage == thisPage ? "hide" : string.Empty;
}
<div class="foundation-story-block #hiddenClass">
<div class="hovereffect">
<img src="[OUR WEBSITE URL]/stories/#thisImage" class="img-fluid">
<div class="overlay">
<h2>#node.Title</h2>
</div>
</div>
</div>

Why does my root Umbraco page have no Children only when it is the currently active page?

I have the following page structure in Umbraco:
Home
Device Management
Resources
Manuals
Downloads
Terms & Conditions
I have a navigation menu as a partial in my Master.cshtml template
which is the base template of all of these pages. In this partial I
call Model.Content.Site().Children to get all the "Device
Management" and "Resources" nodes then recurse to get any descendant
nodes of these.
This all worked fine when all of the pages used the same Document Type
(ChildNodeSelectionPage) but now I've changed the Home page to use a
derived form of this document type (CustomChildNodeSelectionPage)
for an additional property and had to create a dummy controller
(CustomChildNodeSelectionPageController) in my project which derives
from the original ChildNodeSelectionPageController so that the
Master model still gets passed back to the view.
This part also seems to work fine (based upon breakpoints being hit in
the original controller) but the problem happens when the view is hit:
now, for some unknown reason, Model.Content.Site().Children has a
count of 0 when on the Home page but yet, if navigating to the URL of
any of the child pages it's 2 (as expected).
Also note that the Home Document still uses the
ChildNodeSelectionPage.cshtml Template even though its Document Type
has now been changed to use "Custom Child Node Selection Page").
Master.cshtml (note: irrelevant code omitted for brevity)
#inherits Umbraco.Web.Mvc.UmbracoViewPage<Web.Portal.Models.Master>
<!DOCTYPE html>
<html>
<body>
#Html.Partial("Navigation Menu")
#RenderBody()
</body>
</html>
Navigation Menu.cshtml
#inherits Umbraco.Web.Mvc.UmbracoTemplatePage
#using Umbraco.Web
#using Umbraco.Web.Models
#helper AddMenuItems(IEnumerable<IPublishedContent> menuItems)
{
if (menuItems.Any())
{
<ul>
#foreach (var menuItem in menuItems)
{
<li>
#if (menuItem.Id == UmbracoContext.PageId)
{
#menuItem.Name
}
else
{
#menuItem.Name
}
#AddMenuItems(menuItem.Children)
</li>
}
</ul>
}
}
//NOTE: This is where the problem is when called from the `CustomChildNodeSelectionPage`.
#AddMenuItems(Model.Content.Site().Children)
ChildNodeSelectionPage.cshtml
#inherits Umbraco.Web.Mvc.UmbracoTemplatePage
#{
Layout = "Shared/Master.cshtml";
}
//NOTE: Irrelevant code omitted for brevity.
ChildNodeSelectionPageController
public class ChildNodeSelectionPageController : RenderMvcController
{
private ActionResult Index(IPublishedContent content, CultureInfo currentCulture)
=> CurrentTemplate
(
new Master
(
content,
currentCulture,
new Company(0, "ACME"),
new[]
{
new Company(0, "ACME"),
new Company(1, "Jimbo Jones' Jimbo Burgers Inc.")
},
"Jiminy Jilikers"
)
);
public override ActionResult Index(RenderModel model)
=> Index
(
model.Content,
model.CurrentCulture
);
}
CustomChildNodeSelectionPageController
//Exists only so that the new Document Type can call Index on ChildNOdeSelectionPageController.
public class CustomChildNodeSelectionPageController : ChildNodeSelectionPageController
{
}
So it turns out all I had to do was manually republish ("Save and Publish" button on "Content") every page through the CMS and it magically started working again! Prior to this issue, I did have to change the Content Type of the root page via the database which seemed to create a few problems (of which I'm guessing this was one of them).
If anyone's interested, I got the idea to republish from here: https://our.umbraco.com/forum/templating/templates-and-document-types/4585-Change-document-type

File upload drag and drop with dropzone.js

I'm using dropzone.js to implement a drag and drop feature to upload a file. And it's working. What I don't understand is why I can't make it to limit the filesize and the file extension. I have the code for it, I think, but it just won't do it.
In the cshtml page:
<script src="~/js/dropzone.js"></script>
<link href="~/css/dropzone.css" rel="stylesheet" />
<script>
Dropzone.options.dropzone = {
paramName: "file", // The name that will be used to transfer the file
maxFilesize: 2, // MB
acceptedFiles: ".png,.jpg,.gif,.bmp,.jpeg",
accept: function (file, done) {
if (file.name == "justinbieber.jpg") {
done("Naha, you don't.");
}
else { done(); }
}
};
</script>
<div class="row">
<div class="col-md-9">
<div id="dropzone">
<form action="Upload" class="dropzone needsclick dz-clickable" id="uploader">
<div class="dz-message needsclick">
Drop files here or click to upload.<br>
</div>
</form>
</div>
</div>
</div>
And in the HomeController
[HttpPost]
public async Task<IActionResult> Upload(IFormFile file)
{
var uploads = Path.Combine(_environment.WebRootPath, "images");
if (file.Length > 0)
using (var fileStream = new FileStream(Path.Combine(uploads, file.FileName), FileMode.Create))
await file.CopyToAsync(fileStream);
return RedirectToAction("About");
}
I mean, the code is given by them, I have the dropzone.js and the dropzone.css like they tell us to do and still... The thing is, it's working. It uploads, goes to the code behind. Everything. I just can't apply those limitations, even though they are there...
Does anyone have an idea of what's going on here?
You've set the options on a different Dropzone than the form. From the docs:
Dropzone will find all form elements with the class dropzone, automatically attach itself to it, and upload files dropped into it to the specified action attribute.
So your <form>, with class dropzone, is automatically set up as a Dropzone.
Again from the docs, to set options on a Dropzone, you use Dropzone.options.myAwesomeDropzone = {}, where:
// "myAwesomeDropzone" is the camelized version of the HTML element's ID
In your code, you're using Dropzone.options.dropzone, so you're applying those options to a Dropzone with id dropzone. In your HTML, that's a <div>, not your existing <form> Dropzone. So when you drop a file onto your <form>, those options you've specified are not relevant - they're for a different Dropzone (which is actually never instantiated)!
Get rid of your <div id="dropzone">, and change the identifier on your options to Dropzone.options.uploader, so they apply to your form Dropzone.

Issue in Hiding JQGRID when No data Returned

I was following this link to hide JQGRID when No Data Returned How can I hide the jqgrid completely when no data returned?
Am using the below code in .cshtml
<div id="gridWrapper">
<table id="list1">
</table>
<div id="pager">
</div>
</div>
<div id="noSearchResults">
No records Found!
</div>
and in GridComplete
gridComplete: function () {
var recs = parseInt($("#list1").getGridParam("records"), 10);
if (isNaN(recs) || recs == 0) {
$("#gridWrapper").hide();
$("#noSearchResults").Show();
}
else {
$('#gridWrapper').show();
$("#noSearchResults").Hide();
}
}
But when $("#noSearchResults") is executed result is not as expected am getting Error catch in Jqgrid file. What i am doing wrong ?
There is no error in your code except Hide should be hide and Show should be show change this, if you getting error then provide your grid definition.

How can I not render a button in my view if a given property off my model has no value?

I'm new to web development. In my view, I want to conditionally show a button if Model.TemplateLocation (which is a string) is not null or empty. Below is the code that is rendering the button currently:
<div class="WPButton MyButton">
<%=Html.ActionLink(Model.TemplateLinkName, "DownloadTemplate", "ExternalData", new ArgsDownloadTemplate { Path = Model.TemplateLocation, FileName = Model.TemplateFileNameForDownload }, new{})%>
</div>
Can I wrap some C# code in the <% %>'s to test for Model.TemplateLocations value before I render that? I was told to look into #style = "display:none" somehow. Could that be what I need?
You can add control statements in code blocks to conditionally output HTML.
<% if (Model.SomeCondition) { %>
<div>
<ul>
<%=Html.ActionLink(Model.TemplateLinkName, "DownloadTemplate", "ExternalData", new ArgsDownloadTemplate { Path = Model.TemplateLocation, FileName = Model.TemplateFileNameForDownload }, new{})%>
</ul>
</div>
<% } %>
Incidentally, the <%= %> version of the tag is simply a shortcut for a code call to Response.Write(). So this will accomplish the exact same thing:
<% if (Model.SomeCondition) {
Response.Write("<div><ul>");
Response.Write (Html.ActionLink(Model.TemplateLinkName, "DownloadTemplate", "ExternalData", new ArgsDownloadTemplate { Path = Model.TemplateLocation, FileName = Model.TemplateFileNameForDownload }, new{}));
Response.Write("</ul></div>");
} %>
Much debate exists as to the correctness of either method. Many people hate the number of ASP tags they have to use to do the first way, many people feel the second way is incorrect for whatever reason. I find I use both when it offers simpler reading of the code.

Categories