November Happy Hour will be moved to Thursday December 5th.

$$epiforms is not defined when using Form in footer

Vote:
 
@* ... *@ @Html.Raw(Html.RequiredClientResources("Header"))

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:


 
    @* ... *@ 
    @Html.Raw(Html.RequiredClientResources("Header")) 


    @Html.RenderEPiServerQuickNavigator();
    @Html.FullRefreshPropertiesMetaData();
    @Html.RenderPartial("Header", Model); 
    @RenderBody();
    @Html.RenderPartial("Footer", Model); 
    @Html.Raw(Html.RequiredClientResources("Footer"));


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?

#177482
Apr 12, 2017 16:39
Vote:
 

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

#177483
Apr 12, 2017 17:06
Vote:
 

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?

#177485
Apr 12, 2017 17:16
Vote:
 

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.

#177486
Apr 12, 2017 17:49
Vote:
 

Has anyone solved this yet? Having the same problem.

#207056
Sep 06, 2019 22:01
Vote:
 

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.

#207057
Sep 06, 2019 22:02
Vote:
 

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 interestingalthough 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!

#207083
Edited, Sep 10, 2019 0:55
Vote:
 

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?

#207117
Sep 10, 2019 18:00
Vote:
 

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);
			}
		}
#221622
Apr 22, 2020 11:17
Vote:
 

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>
#221625
Apr 22, 2020 13:09
Martin Andersson - May 04, 2020 12:25
Html.RenderPartial is a void method writing on the response. If you want the string you want Html.Partial("Footer", Model); You also don't need @ inside a codeblock. so @{ var footerOutput = @Html.RenderPartial("Footer", Model); }
should be @{ var footerOutput = Html.Partial("Footer", Model); }

Otherwise this works fine.
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.