SaaS CMS has officially launched! Learn more now.

Selection factory with CSS classes

Jon
Jon
Vote:
 

Hi,

I am using a selection factory to set a block type property as a CSS class.

The problem I have is that in the future the class name could change, for example if we change front end framework. In the example below the class name is given as 'button-blue' but could in the future be 'button-background-blue' but any existing instances of the block would continue to use the old classname.

Is there a better way I can write this to handle future class name changes?

Example code -

Class

public class ButtonStyleOptions : ISelectionFactory
{
    public IEnumerable<ISelectItem> GetSelections(ExtendedMetadata metadata)
    {
        return new[]
        {
        new SelectItem {Text = "Blue", Value = "button-blue"},
        new SelectItem {Text = "Black", Value = "button-black"},
        new SelectItem {Text = "White", Value = "button-white"},
        };
    }
}

Block type property

[Display(
    Name = "Button Style"
 )]
 [SelectOne(SelectionFactoryType = typeof(ButtonStyleOptions))]
 public virtual string ButtonStyle { get; set; }

View -

<button class="btn @ButtonStyle">
    @Html.PropertyFor(m => m.ButtonText)
</button>
#247775
Edited, Jan 27, 2021 17:27
Vote:
 

One thing you could do is instead of hardcoding the text and values in the selection factory, you could put them as a configurable item in start page or a settings page so the value is configurable and dynamic.

Here is what I have used.

  1. Have a class defined ButtonOptions with properties text and value.
  2. In the start page or a setting page you would use 
  3.         [EditorDescriptor(EditorDescriptorType = typeof(CollectionEditorDescriptor<EventTypes>))]
            public virtual IList<ButtonOptions> ButtonTypeOptions { get; set; }
  4. Have the text and values live here
  5. Then in the selection factory return startpage.ButtonTypeOptions?.Select(x => new SelectItem { Text = x.ButtonTypeText, Value = x.ButtonTypeValue });

So if there is a change in the value you can update it on page and should reflect wherever you are using.

#247777
Jan 27, 2021 18:44
Vote:
 

We normally create a separate Setting Page as Dileep suggested or you can use Translation strings to dynamically define these values (only use it if you are using dynamic translation provider) 

#247976
Feb 01, 2021 22:32
Jon
Vote:
 

Hi both,

Thanks for the responses. I've tried to set this up as a configurable property on the Start Page...

I’ve created a class of ButtonOption

public class ButtonOption
    {
        public string Text { get; set; }
        public string Value { get; set; }
    }

 In my Start Page I’ve added the following (EditorDescriptor with button options and values defined beneath

[EditorDescriptor(EditorDescriptorType = typeof(CollectionEditorDescriptor<ButtonOption>))]
public virtual IList<ButtonOption> ButtonTypeOptions { get; set; }

public IList<ISelectItem> ButtonOption()
    {
    return new ISelectItem[]
        {
            new SelectItem {Text = "Blue", Value = "button-blue"},
            new SelectItem {Text = "Black", Value = "button-black"},
            new SelectItem {Text = "White", Value = "button-white"},
        };
    }

Selection Factory like so – 

public IEnumerable<ISelectItem> GetSelections(ExtendedMetadata metadata)
    {
        var startPage = _contentLoader.Get<StartPage>(ContentReference.StartPage);
        return startPage?.ButtonTypeOptions.Select(x => new SelectItem { Text = x.Text, Value = x.Value });
    }

 Block property -

public class MyBlock : SiteBlockData
    {
        [Display(Name = "Button type options", Order = 10)]
        [SelectOne(SelectionFactoryType = typeof(ButtonOptionsSelectionFactory))]
        [BackingType(typeof(PropertyString))]
        public virtual string ButtonTypeOptions { get; set; }

When I run my solution though I get the following –

Type 'System.Collections.Generic.IList`1[[Domain.Models.Pages.ButtonOption,Domain, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]' could not be mapped to a PropertyDefinitionType

I had some trouble with the setup in the Start Page so I guess that's where i'm going wrong - appreciate any further help. Thanks.

#248014
Edited, Feb 02, 2021 11:39
Vote:
 

Try defining property like 

        [Display(Name = "Button type options", Order = 10)]
        [SelectOne(SelectionFactoryType = typeof(YourSelectionfactory))]
        [BackingType(typeof(PropertyString))]
        public virtual string ButtonTypeOptions { get; set; }
#248015
Feb 02, 2021 11:47
Jon
Vote:
 

Thanks Naveed - still the same problem i'm afraid. Added the property as below -

public class MyBlock : SiteBlockData
    {
        [Display(Name = "Button type options", Order = 10)]
        [SelectOne(SelectionFactoryType = typeof(ButtonOptionsSelectionFactory))]
        [BackingType(typeof(PropertyString))]
        public virtual string ButtonTypeOptions { get; set; }
#248016
Edited, Feb 02, 2021 12:00
Vote:
 

Hi Jon

tested following solution in my local environment and it works

Start Page (or Settings page) property

[Display(Name = "Button Options List", GroupName = SystemTabNames.Content, Order = 220)]
        [EditorDescriptor(EditorDescriptorType = typeof(CollectionEditorDescriptor<ButtonOption>))]
        public virtual IList<ButtonOption> ButtonOptionList { get; set; }

Button Options property

public class ButtonOption
    {
        public string Text { get; set; }
        public string Value { get; set; }
    }
    [PropertyDefinitionTypePlugIn]
    public class ButtonOptionProperty : PropertyList<ButtonOption>
    {
    }

Selection Factory to get options from Start page (or settings page)

public class ButtonOptionsSelectionFactory : ISelectionFactory
    {
        private readonly Injected<IContentLoader> _contentLoader;
        public virtual IEnumerable<ISelectItem> GetSelections(ExtendedMetadata metadata)
        {
            var startPage = _contentLoader.Service.Get<PageData>(ContentReference.StartPage) as HomePage;
            return startPage?.ButtonOptionList.Select(x => new SelectItem { Text = x.Text, Value = x.Value });
            
        }
    }

Finally the property in your block

[Display(Name = "Button type options", Order = 10)]
        [SelectOne(SelectionFactoryType = typeof(ButtonOptionsSelectionFactory))]
        [BackingType(typeof(PropertyString))]
        public virtual string ButtonTypeOptions { get; set; }

I hope that helps

#248021
Feb 02, 2021 13:55
Jon
Vote:
 

Thanks again Naveed. This now works. I can see the new property on the start page and am am able to add some button type options e.g.

I can then access and set these properties in MyBlock.

However, one thing I noticed is that when I update the button type values in Start Page list e.g. change "button-black" to "button-bg-black" - the existing instances of the block continue to use "button-black".

This is my goal here to make sure if any of the values change then existing instances of the block will use the new values.

Feel so close now. Thank you for your help with this.

#248023
Edited, Feb 02, 2021 15:14
Vote:
 

Jon - for your requirement you need a slightly different approach.  

ButtonTypeOptions property stores a string copy of the dropdown value. When you update its values on start page -> ButtonOptionList, it would not update the copied value. All you need to do is store a key instead of value and get the value of key at run time. let me explain to you further

Your selection factory can have hardcoded items

public IList<ISelectItem> ButtonOption()
    {
    return new ISelectItem[]
        {
            new SelectItem {Text = "Blue", Value = "blue-key"},
            new SelectItem {Text = "Black", Value = "black-key"},
            new SelectItem {Text = "White", Value = "white-key"},
        };
    }

On the Start page, you will create a similar IList property that takes name & value but this time your name is key and value is actual CSS class.

such as

Name Value
blue-key    button-blue
black-key button-black

Now when you render block, your ButtonTypeOptions property will give you value "blue-key" and you will check Start Page-> ButtonOptionList to get actual value of CSS ("button-blue")

#248033
Feb 02, 2021 16:36
Jon
Vote:
 

Yes! Thank you very much Naveed. Working exactly as hoped for.

The last bit, as you mentioned, was to check the home page list of keys against the key value option selected in the block, which i've done in MyBlockController something like so -

private string SetButtonOption(string BlockBackgroundSelectedOption)
    {
        var ButtonBackgroundColors = _contentLoader.Service.Get<StartPage>(ContentReference.StartPage).ButtonBackgroundColors;
        var BlockBackgroundColor = ButtonBackgroundColors.Where(x => x.Key == BlockBackgroundSelectedOption).FirstOrDefault().Value;
        return BlockBackgroundColor;
    }

Now any changes to the home page button option values is reflected any instances of MyBlock.

Thanks again.

#248086
Feb 03, 2021 16:44
Vote:
 

That's great - You might want to put it in separate Helper class so that it can be used by other blocks/pages.

#248087
Feb 03, 2021 17:05
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.