I don't use Html.Raw, I have simplified:
<html> <head> @Html.RequiredClientResources("Header") </head> <body> @Html.RenderEPiServerQuickNavigator() @RenderBody() @Html.RequiredClientResources("Footer") </body> </html>
BR,
Marija
I don't think that the answer makes sense without
@Html.RenderPartial("Footer", Model);
or is it removing Html.Raw that is the solution to my problem?
Hi, I've misread your question.
Html.Raw doesn't matter much (although it looks like you would get three extra ; on the page)
I don't think it should matter much if it is in the footer, unless you have some scripts there?
I'd suggest you try this quickly on AlloyTech.
I also noticed I do not get this error on pages that have forms in the body, since it loads the script correctly in those cases. Its only on pages that have no forms in the body, but one in the footer that have this issue.
Hmm...interesting, I've never really thought about this before.
I don't think there is a nice fix for this honestly. The layout is executed after RenderBody
, which explains why it isn't working in the footer. To try and put it simply, when it's used in the body the relevant FormContainerBlockController
methods (which register all the necessary JS and CSS resources) are firing before
Html.RequiredClientResources(RenderingTags.Header)
. However, because your footer is presumably in your layout (or a partial or whatever) this is happening after RequiredClientResources(RenderingTags.Header)
.
There are quite a lot of forum posts documenting similar(ish) issues, this one and this one are both kind of interesting—although they don't solve your issue.
If you need to use Episerver Forms in the footer then the only way I can think around the issue is using a global action filter which registers the required resources. Something like this (but better!):
public class FooterFormActionFilter : IActionFilter
{
private readonly IContentLoader _contentLoader;
public FooterFormActionFilter(IContentLoader contentLoader)
{
_contentLoader = contentLoader;
}
public void OnActionExecuting(ActionExecutingContext filterContext)
{
}
public void OnActionExecuted(ActionExecutedContext filterContext)
{
// Determine whether an editor has added a form to the footer.
// Get the Content Reference & try to load the content
if (!_contentLoader.TryGet<FormContainerBlock>(new ContentReference(123), out var formBlock))
{
return;
}
var controller = new FormContainerBlockController();
controller.RegisterScriptResources(formBlock);
controller.RegisterCssResources();
}
}
You could register it the same as the one that's in Alloy.
It's not great, but I can't think of a better solution right now!
Thanks for the feedback. Thats not an ideal solution but its a step in the right direction. Im apprehensive to put a filter in that runs on every page, and I can't think of a way to tell whether the form resources have already been loaded or not. I would be doubling up and adding more overhead to the page load time. Can you think of a way to detect if form resources have been loaded already?
I know this is old ... but a little note to other developers that come in here with same issue.
After some googling I could not find a better solution than this ... other than nesting Layouts ... which was too complex for my existing solution. But ...
Jakes proposed ActionFilter will run on all Actions (could be many depending on how many blocks and partials you have). This could of course be solve by only running the code once and use HttpContent.Items too keep track of this.
Instead you could have the same logic in a static helper you call in the layout just before @Html.RequiredClientResources("Header")
Layout.cshtml:
<title>@(MetaTitle)</title>
@{ RegisterFooterFormsClientResourcesHelper.RegisterFooterFormsClientResources(); }
@Html.RequiredClientResources("Header")
RegisterFooterFormsClientResourcesHelper.cs (note that I do not need the css resources so I removed the registration for those):
public static void RegisterFooterFormsClientResources()
{
var siteSettings = ServiceLocator.Current.GetInstance<ISiteSettings>();
var footerSettings = siteSettings?.GetSettingByBlockType<FooterSettingsBlock>();
if (footerSettings == null)
{
return;
}
var forms = footerSettings.FooterBannerContentArea?.GetFilteredItemsOfType<ModalContainerBlock>().SelectMany(m => m.FormArea.GetFilteredItemsOfType<FormContainerBlock>()).ToList();
if (forms == null || !forms.Any())
{
return;
}
var controller = new FormContainerBlockController();
foreach (var formContainerBlock in forms)
{
controller.RegisterScriptResources(formContainerBlock);
}
}
Or an even better solution (thanks Thomas S. ...). So the rendering of the footer takes place before outputting the required client resources for header ...
<html>
<head>
@{ var footerOutput = @Html.RenderPartial("Footer", Model); }
@Html.RequiredClientResources("Header")
</head>
<body>
@Html.RenderEPiServerQuickNavigator()
@RenderBody()
@Html.Raw(footerOutput)
@Html.RequiredClientResources("Footer")
</body>
</html>
I get this javascript error when adding a EPiServer Forms form to the footer. I guess it has to do with something not loading in the correct order but I can not figure it out.
My Layout.cshtml has the following simplified layout:
And the footer is containing a regular ContentArea with a Form in it.
Is it some obvious error here? Or how should the correct loading order be?