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

Rewriting views doesn't work for default form elements

Vote:
 

In CMS 11 we had it set up like this: we have custom block derived from FormContainerBlock, "CustomFormContainerBlock" (for example) with a default controller and for that block there's a folder in views sevtion - named "CustomFormContainerBlock". In there we have custom views for generic form elements, namely TextboxElementBlock.cshtml (as per naming in \modules\_protected\EPiServer.Forms\EPiServer.Forms.zip). This view is being picked up when rendering said from block.

Now in CMS 12 we have the same setup, but custom TextboxElementBlock.cshtml is not being used when rendering form block, the view from \modules\_protected\EPiServer.Forms\EPiServer.Forms.zip is being used.

Weird: We also have custom form elements and custom views for there elements in "CustomFormContainerBlock" folder together with default elements' views. They are bieng used.

Also weird: we have custom view engine and in debug I see that for custom form elements it goes through FindView method but default elements never go through here. I could try to adjust view name a that point but since it never goes in the view engine, I can't do that.

Here's the view engine:

    public class CustomViewEngine : RazorViewEngine, IViewEngine
    {
        public CustomViewEngine() : base(
                ServiceLocator.Current.GetInstance<IRazorPageFactoryProvider>(),
                ServiceLocator.Current.GetInstance<IRazorPageActivator>(), 
                ServiceLocator.Current.GetInstance<HtmlEncoder>(), 
                ServiceLocator.Current.GetInstance<IOptions<RazorViewEngineOptions>>(),
                ServiceLocator.Current.GetInstance<ILoggerFactory>(), 
                ServiceLocator.Current.GetInstance<DiagnosticListener>())
        {
        }
        
        public new ViewEngineResult FindView(ActionContext context, string viewName, bool isMainPage)
        {
            var newViewName = viewName.Replace(@"\", "/").TrimStart('/');
            if (newViewName.TrimStart('/').StartsWith("Views/"))
            {
                newViewName = $"~/Areas/{context.RouteData.DataTokens["area"]}/{newViewName}";
            }

            if(newViewName.TrimEnd('/').EndsWith("FormContainerBlock"))
            {
                newViewName = newViewName.TrimStart('/').Replace("Components/", string.Empty);
            }

            if (newViewName.TrimStart('/').StartsWith("DisplayTemplates/"))
            {
                newViewName = $"~/Areas/{context.RouteData.DataTokens["area"]}/Views/Shared/{newViewName}";
            }

            var result = base.FindView(context, newViewName, isMainPage);
            return result.Success 
                ? result 
                : GetView(null, newViewName, isMainPage);
        }

        public new ViewEngineResult GetView(string executingFilePath, string viewPath, bool isMainPage)
        {
            var newViewPath = viewPath.Replace(@"\", "/");
            return base.GetView(executingFilePath, newViewPath, isMainPage);
        }
    }

So how to override views for form elements in CMS 12?

Is this a bug?

#282006
Jun 17, 2022 11:56
Vote:
 

Hi Ivan,

There is an example in the Foundation project that overrides the existing TextboxElementBlock view. There are a couple of options — I haven't tried them all but hopefully it helps you with finding a solution.

  1. Use the Foundation example and add the path in a IViewLocationExpander. I think this is the standard way to do it and it can potentially replace your custom view engine.
    https://github.com/episerver/Foundation/blob/main/src/Foundation/Infrastructure/Display/FeatureViewLocationExpander.cs
  2. Update the views path for form elements in the Forms.config file (this does not override the default path used internally in the CMS).
    https://docs.developers.optimizely.com/content-cloud/v1.2.0-forms/docs/configuring-optimizely-forms#default-location-for-view-templates
  3. Create a class that inherits from CustomViewLocationBase. It enables you to specify the rendering order of the paths.
    https://docs.developers.optimizely.com/content-cloud/v1.2.0-forms/docs/searching-block-templates

Cheers 😁

#282125
Edited, Jun 20, 2022 1:43
Vote:
 

Hi Ynze, thank you for yuor reply, very helpful.

However, all examples that you gave only let me specify new generic views for the entire project. I need to specify views for specific type of FormContainerBlock. Like it worked in CMS 11.

#282141
Jun 20, 2022 12:26
Vote:
 

I think Ynze's post could still help you.

I'm not sure about specific elements not appearing in the ExpandViewLocations method but I believe the FormContainerBlock does get additional views by the RazorViewEngine through here.

When the ExpandViewLocations method gets executed, you could look for FormContainerBlock (or whatever your custom Block is called) against context.ViewName.

That way you can then add the extra view locations depending on whether its a CustomFormContainerBlock, SpecialFormContainerBlock, etc.

I've not tried it so apologies if you end up down the wrong path with this one. I am making the assumption all form elements get their additional view locations when its processing the FormContainerBlock.

#282194
Jun 21, 2022 18:34
Vote:
 

Actually I couldn't replace default form elements views at all, not with any example that Ynze provided. I tried implementing IViewLocationExpander, ICustomViewLocation and editing location in forms.config. I know I set them up correctly because I'm getting hits in debug for IViewLocationExpander and ICustomViewLocation but my views for TextboxElementBlock are never used.

I also checked out and launched test foundation website. It has no forms created so I created one and in the custom view add "TEST" text in label. Form is rendering but "TEST" label is not showing up.

So Foundation example does not have a working way to replace form element views.

I assume that current veresion of forms does not have a way to replace element views at all.

#282237
Jun 22, 2022 9:14
Vote:
 

This made curious so I have it a go too....yes looks like Foundation's example are not working.

Also the ExpandViewLocations method isn't being hit for any Elements when I force an immediate return in the Populate Values method...

#282243
Jun 22, 2022 10:59
Ivan Maslov - Jun 22, 2022 11:24
ExpandViewLocations runs during startup
Vote:
 

Ivan so I think I've found the issue..and a small work around.

In Foundation, the custom element views are stored in /Feature/Shared/Views/Elements folder.

The configuration for this is in Forms.Config against the variable formElementViewsFolder.

However when you change this and restart the app, the code isn't honouring this.

So instead I created the folder structure /Views/Shared/ElementBlocks/ to match what is in the config

My customized choice element block is now being picked up as the main one:

EDIT: I've just realized you don't want a generic solution for the elements, you want specific element views for different form containers. Ignore this post, I'll keep it here in case its useful to someone else.

#282326
Edited, Jun 23, 2022 10:43
Vote:
 

Surjit, yes, that worked, thanks!

And yes, it is a workaround and won't look pretty since we don't even have Views folder in root.

And it still doesn't allow me to rewrite views per container.

#282327
Jun 23, 2022 10:53
Vote:
 

Hey thanks for bringing this to our attention.  I have updated Foundation to work properly now.  The FormContainerBlock works a little different and needs to be under Components/FormContainerBlock based of the path in the forms.config.  Please confrim this works for you now.

#282350
Jun 23, 2022 22:22
Vote:
 

Mark, yes, it works now. Although since you had to edit Forms.config, I'd also remove the path from FeatureViewLocationExpander for now (it could be confusing), leave a todo maybe?

And it still doesn't allow me to rewrite views per container.

#282415
Edited, Jun 24, 2022 8:10
* 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.