Dynamic Properties for Widget

Carsten Gerwing asked on December 3, 2018 14:32

Hi everyone,

since the release of Kentico 12 I've tried some experiments with the MVC version. Right now I would love to build a widget where the editor can decide how many Icons should be displayed/editable, ranging from one to undefined. I've been building the following model: The widget exists out of the "Amount" of icons and "Columns" as well as a list of "icons". The icons themselves are 2 strings "ClassName" and "DescriptionText". Based on the "Amount" the editor should be able to decide how many icons he desires. I've been able to display the model dynamically but somehow I can't save the changes to the properties. Where did I go wrong?

Help would be greatly appreciated :)

WIDGET MODEL

using System.Collections.Generic;
namespace DancingGoat.Models.Widgets.IconWallWidget
{
    public class IconWallWidgetViewModel
    {

        public IconWallWidgetViewModel() {
            Icons = new List<Icon>();
        }
        public string Columns { get; set; }
        public int Amount { get; set; }
        public List<Icon> Icons { get; set; }


    }
    public class Icon
    {
        public string ClassName { get; set; } = "";
        public string DescriptionText { get; set; } = "";
    }
}

WIDGET PROPERTY MODEL

using System.Collections.Generic;
using Kentico.Forms.Web.Mvc;

using Kentico.PageBuilder.Web.Mvc;

namespace DancingGoat.Models.Widgets.IconWallWidget
{
    public sealed class IconWallWidgetProperties : IWidgetProperties
    {
        public IconWallWidgetProperties()
        {
            Icons = new List<PropertyIcon>();
        }
        [EditingComponent(IntInputComponent.IDENTIFIER, Order = 0, Label = "Columns")]
        public string Columns { get; set; }
        [EditingComponent(IntInputComponent.IDENTIFIER, Order = 0, Label = "Amount")]
        public int Amount { get; set; } = 1;

        public List<PropertyIcon> Icons { get; set; }
    }
    public class PropertyIcon
    {
        public string ClassName { get; set; } = "";
        public string DescriptionText { get; set; } = "";
    }
}

WIDGET CONTROLLER

using System.Web.Mvc;
using Kentico.PageBuilder.Web.Mvc;

using DancingGoat.Controllers.Widgets;
using DancingGoat.Models.Widgets.IconWallWidget;

[assembly: RegisterWidget("Kentico.Content.IconWallWidget", typeof(IconWallWidgetController), "Icon", Description = "description IconWall", IconClass = "icon-l-text")]

namespace DancingGoat.Controllers.Widgets
{
    public class IconWallWidgetController : WidgetController<IconWallWidgetProperties>
    {
        // GET: IconWallWidget
        public ActionResult Index()
        {
            // Retrieves the properties as a strongly typed object
            IconWallWidgetProperties properties = GetProperties();

            if(properties.Icons.Count == 0)
            {
                for(int i = 0; i < properties.Amount; i++)
                {
                    PropertyIcon EmptyIcon = new PropertyIcon
                    {
                        ClassName = "",
                        DescriptionText = ""
                    };
                    properties.Icons.Add(EmptyIcon);
                }
            }

            // Creates a new model and sets its value
            var model = new IconWallWidgetViewModel {
            Amount = properties.Amount,
            Columns = properties.Columns
            };

            for (int i = 0; i < properties.Amount; i++)
            {
                var currentIcon = new Icon();
                currentIcon.ClassName = properties.Icons[i].ClassName;
                currentIcon.DescriptionText = properties.Icons[i].DescriptionText;

                model.Icons.Add(currentIcon);
            }

            return PartialView("Widgets/_IconWallWidget", model);
        }
    }
}

WIDGET VIEW

@using Kentico.PageBuilder.Web.Mvc
@using Kentico.Web.Mvc

@using DancingGoat.Models.InlineEditors
@using DancingGoat.Models.Widgets.IconWallWidget

@model IconWallWidgetViewModel


<p>Fixed Values ATM</p>
<p>Columns: @Model.Columns</p>
<p>Icon: @Html.Raw(Model.Icons)</p>

@if (Context.Kentico().PageBuilder().EditMode)
{


    for (int index = 0; index < Model.Amount; index++)
    {
        <p>Edit Classname</p>
        Html.RenderPartial("InlineEditors/_TextEditor", new TextEditorViewModel
        {
            PropertyName = ??? ,
            Text = Model.Icons[index].ClassName
        });
        <p>Edit Description</p>
        Html.RenderPartial("InlineEditors/_TextEditor", new TextEditorViewModel
        {
            PropertyName =???,
            Text = Model.Icons[index].DescriptionText
        });
    }

}
else
{

    foreach (var icon in Model.Icons)
    {
        <p>Raw HTML Content for Icon Number X</p>
        @Html.Raw(icon.ClassName);
        @Html.Raw(icon.DescriptionText);
    }
} 

Recent Answers


Jan Lenoch answered on December 4, 2018 13:05

Hi Carsten!

Great to see you've jumped into developing with our Page builder!

As we always strive to understand the deeper context, let me ask: Why do you need to post the count of icons via your Amount property? What's the overall purpose of the Columns property?

Anyways, I've noticed you missed a properties retriever and current page retriever in your controller. You might want to write an empty constructor that accepts the above two objects and pass them onto the constructor of the base class. Like so:

public IconWallWidgetController(IWidgetPropertiesRetriever<IconWallWidgetProperties> propertiesRetriever,
    ICurrentPageRetriever currentPageRetriever) : base(propertiesRetriever, currentPageRetriever)
{
}

I hope it helps.

Jan

0 votesVote for this answer Mark as a Correct answer

Juraj Ondrus answered on December 4, 2018 13:08

Hi,
What is the entire implementation? I just followed the sample from the widget development documentation and it is working fine.

0 votesVote for this answer Mark as a Correct answer

Carsten Gerwing answered on December 4, 2018 14:24 (last edited on December 4, 2018 14:27)

Hi Jan, as mentioned before, I have the following Idea in mind: Based on the integer in the Amount property the Editor can display and edit more or less Icons as he desires, i.e amount = 5 thus the editor can edit the properties of 5 icons that should be displayed, if amount = 1 only one icon can be displayed/modified. As of now the Columns property has no function, that is for future usage.

Regarding your properties retriever, I do not understand why I would need it as my controller is using the WidgetControllerIconWallWidgetProperties> Interface which allows me access to the GetProperties Method based on a TPropertiesType and return the Type TPropertiesType. That means I already have the properties accessible in my controller. If I indeed would need the IWidgetPropertiesRetriever why is this nowhere mentioned in the documentation/example?

As for the answer from Juraj, thank you for your help unfortunately this has nothing to do with the sample widget from the documentation. More or less it's the basic setup of a widget I require and that means dynamically increase the properties. As I was saying, right now I'm able to work this out with the standard Model via a List. The Problem I have right now is saving the properties correctly (when the editor want to save his changes).

Thanks again Carsten

0 votesVote for this answer Mark as a Correct answer

Jan Lenoch answered on December 4, 2018 15:26

Hi Carsten,

Thanks for clarifying the use of the Amount and Columns properties. I was mainly asking to be sure they are not supposed to interact with the rest of your code.

My advice to get an instance of the properties retriever was driven by the fact that the WidgetController.GetProperties method in turn uses the property retriever. Without the retriever instance at hand, the WidgetController would have no means of getting the properties off of route data or request body.

Jan

0 votesVote for this answer Mark as a Correct answer

   Please, sign in to be able to submit a new answer.