Virtual Happy Hour this month, Jun 28, we'll be getting a sneak preview at our soon to launch SaaS CMS!

Try our conversational search powered by Generative AI!

Handle property fallbacks

Vote:
 

Hi,

What is your best approach to handle property fallbacks in your Webforms projects? This is very easy to handle in an MVC project, just override the get method in your content type model. However, this doesn't work with Webforms since the property webcontrol doens't use the model to fetch the property value. Instead it work against the PropertyDataCollection directly.
This is how I usually handle it when I want the PageHeading to have PageName as fallback:

[PagePlugIn(
    "Property fallbacks",
    "Handles fallbacks for properties on a global basis.")]
public class PropertyFallbacks
{
    private static Hashtable FallbackProperties { get; set; }

    public static void Initialize(int optionFlags)
    {
        PropertyDataCollection.GetHandler = FallbackPropertyHandler;

        FallbackProperties = new Hashtable { { "PageHeading", "PageName" } };
    }

    public static PropertyData FallbackPropertyHandler(string name, PropertyDataCollection properties)
    {
        var data = properties.Get(name);

        if (data != null && (!data.IsNull || data.IsMetaData))
        {
            return data;
        }

        var dataFromOtherPage = PropertyGetHandler.FetchDataFrom(name, properties);

        if (dataFromOtherPage != null && dataFromOtherPage.Value != null)
        {
            return dataFromOtherPage;
        }

        if (FallbackProperties.ContainsKey(name))
        {
            var dataFallback = FallbackPropertyHandler(FallbackProperties[name].ToString(), properties);

            if (dataFallback != null && dataFallback.Value != null)
            {
                return dataFallback;
            }
        }

        return DynamicPropertyCache.DynamicPropertyFinder.FindDynamicProperty(name, properties) ?? data;
    }
}


But now I've run into some issues. E.g. when using the Language add-on and when the editor choose to copy the English content to another language. Then the PageHeading property will get a value, even though the English version didn't have a value in the PageHeading propery.

#146744
Mar 22, 2016 13:15
Vote:
 

I use a separate viewmodel in many (often older) webforms projects where I collect everything special special that should be displayed on page including css classes etc and regard the logic necessary as part of presentation logic. So in my case it would go in my GetDefaultViewmode() method where I create my viewmodel. If it's a common problem on many pages, I would add an interface to pagetypes with GetHeading or similar and use that instead for getting of heading.  My viewmodels normally don't contain currentpage default properties, I use currentpage straight off for that to save some typing. If I need to add some logic to it or get from external data sources I gather everything to a viewmodel kind of object and use that instead. Been a while now since I had to get dirty with webforms though :) 

#146751
Mar 22, 2016 15:12
Vote:
 

Still, that approach doesn't work with the property webcontrol and on-page-edit. Otherwise I could simply do:

<%: CurrentPage.PageHeading ?? CurrentPage.Name %>
#146752
Mar 22, 2016 15:18
Vote:
 

For dope support and webforms, custom renderers is an option

http://world.episerver.com/Blogs/Linus-Ekstrom/Dates/2012/10/Custom-renderers-for-properties/

or simply connect your control to your property with 

someControl.ApplyEditAttributes<StartPage>(x => x.Links);

in code behind. Depends on what version you are using...

To be honest, I usually skip that part for some properties like that. Depends on the customer of course. EPiServer 6 and before wasn't very focused on dope support and I never heard a complaint about it.

From EPiServer 7 it's more or less required though :)

#146755
Edited, Mar 22, 2016 16:45
Vote:
 

Creating a custom rendering control sure does the job, I think. But that won't solve the problem if a devloper is trying to access the property from an indexer, e.g. CurrentPage["PageHeading"]. But that might not be a problem.

We already have a bunch of custom rendering controls, so one more won't hurt :)

#146756
Mar 22, 2016 17:00
Vote:
 

:)

#146757
Mar 22, 2016 17:17
Vote:
 

This is how I solved it. You need to decorate the property in the content type with the UI hint UIHints.Heading (a constant with the value 'heading'). I also added an extra guard and checked the property name, which probably is a bit overkill and not so nice to have hard-coded.

[TemplateDescriptor(
    Default = true,
    Inherited = true,
    TagString = UIHints.Heading)]
public class HeadingControl : PropertyStringControl, IRenderTemplate<string>, IRenderTemplate
{
    protected override bool ShouldCreateDefaultControls()
    {
        // We should always create default controls if we're rendering
        // PageHeading, since we will fall back on PageName which always
        // has a value.
        if (this.IsPageHading())
        {
            return true;
        }

        return base.ShouldCreateDefaultControls();
    }

    public override void CreateDefaultControls()
    {
        this.Controls.Add(this.GetControl());
    }

    public override void CreateOnPageEditControls()
    {
        this.Controls.Add(this.GetControl());

        if (this.PropertyIsEditableForCurrentLanguage())
        {
            this.ApplyEditAttributes();
        }
    }

    private Control GetControl()
    {
        var tag = this.CustomTagName ?? "h1";

        var value = this.ToWebString();

        // Fall back on PageName if PageHeading is null
        if (string.IsNullOrEmpty(value) && this.IsPageHading())
        {
            var page = this.CurrentContent as PageData;

            if (page != null)
            {
                value = page.Name;
            }
        }

        var control = new HtmlGenericControl(tag)
        {
            InnerHtml = value
        };

        this.CopyWebAttributes(control);

        return control;
    }

    private bool IsPageHading()
    {
        return this.PropertyData.Name.Equals("PageHeading");
    }
}
#146759
Mar 22, 2016 17:51
Vote:
 

Looks good!

#146765
Mar 23, 2016 9:54
This topic was created over six months ago and has been resolved. If you have a similar question, please create a new topic and refer to this one.
* 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.