If you’ve been following this blog, you know that we now have a functioning MVC site with some dynamic content stored in a Kentico installation. We’ve preconfigured the site for widgets, applied a design, and some script bundling. In this article, I’m going to show you how to create a custom widget within your sites and use it in an Editable Area. Let’s get to it!
TL;DR Version
- You can use the Media Selection form control to upload files for page types.
- MVC Widgets consist of several files that all work together to provide the functionality (Properties, View Models, Controllers, and Views). Be sure to understand each component to see how MVC Widgets work within your Kentico sites.
- MVC Widgets can contain supplemental JavaScript and script files, depending on your functionality.
- View Models allow you to manipulate/transform model properties and use them within your controllers and views.
- MVC Widget registration allows you to define the title, description, and icon for each widget. Use these properties to explain the widget and its purpose to your editors.
- The Dancing Goat MVC site contains a few sample widgets. Use these as a guide for your widget creation.
After nearly 20 years as a backend developer, having things work smoothly is my goal. Sure, some amazing designers always make things look great, but I’ve always focused on getting the site coded and my integrations flowing seamlessly. Because MVC is so much more of a hands-on approach to development, there’s a lot of opportunities to blow things up…er…make things work perfectly within your Kentico sites. You’ll have full control over the presentation, but that doesn’t mean editors can’t get in on the fun. With the new Page Builder and Editable Areas, you can allow business users to drag-and-drop content where they need it and really pump up the power on your applications. Let me show you how to add widgets to your MVC site.
MVC Widget Page Type
Before I can get started on widgets, I need some content to display. As I noted in Part 2, there are some important things to know about the content you’re loading with the Page Builder. These deal with searchability and how the content can be translated, so it’s important to understand those first. You’ll see how my content fits within these limitations and why widgets are a good fit for the functionality I want to add.
Page Builder Notes
In my case, I’m going to use a new widget to display MVC Widget content items (and possibly open a wormhole with that vernacular). This means I’m going to need some content for each widget. I create a new MVC Widget page type to fit the bill.
Because this site is all about showcasing widgets, I want users to be able to download the source code for each component. This means I need a field for each widget content item to hold the download package.
With the page type in place, I create some content items for my widgets. One will be the MVC Widget List Widget, which will display all the widgets available for download. The other will be a basic Text Widget component.
Lastly, I create the code class and add the new code to my MVC project.
MVC Widget List Widget
With the page type and content created, I’m ready to create my custom widget. Because we’re doing some pretty fly dynamic loading of components behind the scenes, there are a few pieces you’ll need to make for your new code to work. These will help the system understand what the widget is, what its properties are, and how to handle the functionality and display it. Your widgets may have even more files, depending on what you’re trying to do.
Widget Properties
Your widget Properties class will define the settings your widget is going to use. This file also determines how the user will configure these properties, so we’ve included some standard EditingComponent classes to keep things simple. By using this class, you can select the interface your users will work with when configuring their widgets.
Here’s my MVCWidgetListWidgetProperties class with my settings. Note that I’m using specific types of inputs for the EditingComponent declaration for each property.
public
sealed
class
MVCWidgetListWidgetProperties : IWidgetProperties
{
[EditingComponent(TextInputComponent.IDENTIFIER, Order = 0, Label =
"Header Text"
)]
public
string
HeaderText {
get
;
set
; }
[EditingComponent(TextAreaComponent.IDENTIFIER, Order = 1, Label =
"Intro Text"
)]
public
string
IntroText {
get
;
set
; }
[EditingComponent(IntInputComponent.IDENTIFIER, Order = 2, Label =
"Select Top N"
)]
public
int
SelectTopN {
get
;
set
; } = 3;
[EditingComponent(CheckBoxComponent.IDENTIFIER, Order = 3, Label =
"Show ALL Link"
)]
public
bool
ShowAllLink {
get
;
set
; } =
false
;
}
ViewModel
Next, we need to tell our MVC site how to interact with the data. To do this, I create a new MVCWidgetListWidgetViewModel class to use in my controller/view. This defines the properties I’m going to use when I work with the widget from a business layer perspective. In my case, these match the Properties class, however, you may need to transform your data in your ViewModel to meet your needs.
public
class
MVCWidgetListWidgetViewModel
{
public
string
HeaderText {
get
;
set
; }
public
string
IntroText {
get
;
set
; }
public
IEnumerable<MVCWidget> MVCWidgets {
get
;
set
; }
public
bool
ShowAllLink {
get
;
set
; }
}
Controller
With the Properties and ViewModel created, I’m ready to make my controller. In my case, my widget is just going to pull a list of MVC Widget content items and display them. This means I only need to register the widget with the right settings and add a default ActionResult to pull the data.
For the controller, I pass the new MVCWidgetListWidgetProperties class to the constructor. This allows the controller to retrieve the widget properties.
public
MVCWidgetListWidgetController(IWidgetPropertiesRetriever<MVCWidgetListWidgetProperties> propertiesRetriever,
ICurrentPageRetriever currentPageRetriever) :
base
(propertiesRetriever, currentPageRetriever)
{
}
When you register your widgets, you are setting the title and icon the user is going to see within the Pages module of the Admin site. Be sure to add enough information to explain exactly what the widget will do.
// Assembly attribute to register the widget for the connected Kentico instance
[assembly: RegisterWidget(
"KenticoMVCWidgetShowcase.Widgets.MVCWidgetListWidget"
,
typeof
(MVCWidgetListWidgetController),
"MVC Widget List "
, Description =
"This widget allows editors to insert/edit a list of MVC widgets."
, IconClass =
"icon-kentico"
)]
And here’s the Index action where I retrieve the data. Note that I’m defining a specific Partial View to use to display the Index results.
public
ActionResult Index()
{
try
{
string
culture =
"en-us"
;
string
siteName = SiteContext.CurrentSiteName;
// Retrieves the properties as a strongly typed object
var properties = GetProperties();
// Get the collection of MVC Widgets
IEnumerable<MVCWidget> mvcwidgets = MVCWidgetProvider.GetMVCWidgets()
.OnSite(siteName)
.Culture(culture)
.TopN(properties.SelectTopN)
.OrderByDescending(
"DocumentPublishFrom"
)
.TypedResult;
// Ensures that the result of the query is saved, not the query itself
// Creates a new model and sets its value
var model =
new
MVCWidgetListWidgetViewModel
{
HeaderText = properties.HeaderText,
IntroText = properties.IntroText,
MVCWidgets = mvcwidgets,
ShowAllLink = properties.ShowAllLink
};
return
PartialView(
"Widgets/_MVCWidgetListWidget"
, model);
}
catch
(Exception ex)
{
EventLogProvider.LogException(
"MVCWidgetListWidgetController"
,
"ERROR"
, ex);
return
null
;
}
}
TIP
In your MVC site, you can write any errors/exceptions to the Kentico event log. This allows you to keep track of any problems in your application and find the cause quickly.
View
The last step of the process is to create the view. Because I created the MVCWidgetListWidgetViewModel class that contains all the properties, I can set this as my model and easily pull out the values for each MVC Widget content item. I also apply some of my site template styling to keep things looking great.
@using Kentico.Web.Mvc
@using KenticoMVCWidgetShowcase.Models.Widgets.MVCWidgetListWidget
@model MVCWidgetListWidgetViewModel
@if (Model.HeaderText != null)
{
<
header
class
=
"major"
>
<
h2
> @Model.HeaderText</
h2
>
</
header
>
}
@if (Model.IntroText != null)
{
<
p
>@Model.IntroText</
p
>
}
<
div
class
=
"posts"
>
@foreach (var mvcwidget in Model.MVCWidgets)
{
string screenshoturl = Url.Kentico().ImageUrl(mvcwidget.MVCWidgetScreenshot, SizeConstraint.MaxWidthOrHeight(400));
<
article
>
<
h3
>@mvcwidget.MVCWidgetName</
h3
>
<
a
href
=
"@Url.ForMVCWidget(mvcwidget)"
class
=
"image mvcwidgetscreenshot"
>
<
img
src
=
"@screenshoturl"
alt
=
"@mvcwidget.MVCWidgetName"
/>
</
a
>
<
p
>@mvcwidget.MVCWidgetShortDescription</
p
>
<
ul
class
=
"actions"
>
<
li
><
a
href
=
"@Url.ForMVCWidget(mvcwidget)"
class
=
"button primary"
>Details</
a
></
li
>
</
ul
>
</
article
>
}
</
div
>
@if (Model.ShowAllLink)
{
<
hr
/>
<
div
>
<
ul
class
=
"actions"
>
<
li
><
a
href
=
"/mvcwidgets"
class
=
"button"
>All MVC Widgets</
a
></
li
>
</
ul
>
</
div
>
}
Text Widget
Before I get to testing, I want to add another widget. With Kentico 12, we haven’t added many pre-made widgets out of the box. Because the implementation varies so much from project to project, we can’t predict the optimal way to implement the functionality for everyone. The Dancing Goat MVC sample site does contain a few widgets, one of which is the Text Widget, that I want to include in this site.
Luckily, this widget is already created, and I just need to port it from the Dancing Goat site to my own. This widget does have a few more components however, as it uses an Inline Editor for the interface. This means that users will be able to manipulate the widget properties directly within the Pages models (rather than a modal window), using a pre-defined interface.
Properties/ViewModel/Controller
The Text Widget component contains a Properties, View Model, and Controller, just like the MVC Widget List widget. I copied these files from the Dancing Goat site, updating the namespaces and using statements where necessary.
Inline Editor
The TextWidget widget utilizes the new Inline Editor functionality. This feature allows users to change widget properties right from inside the Page tab in the Admin site. In order to accomplish this, you need to define the InlineEditor to use for the widget. This can include several code files, as well as custom JS and style files.
I copied these files from the Dancing Goat site and added them to my MVC application. I’m not going to dive into these files in this article. Just know that they create an editable text area for the user to add their custom text.
View
The last step for the Text Widget is the partial view. This includes using the new TextWidgetViewModel to extract the currently entered text. If the page is in Edit mode (the user is looking at in the Admin site), then we’ll use the TextEditorViewModel to enable the InlineEditor capability we added.
@using KenticoMVCWidgetShowcase.Models.InlineEditors
@using KenticoMVCWidgetShowcase.Models.Widgets
@model KenticoMVCWidgetShowcase.Models.Widgets.TextWidgetViewModel
@if (Context.Kentico().PageBuilder().EditMode)
{
Html.RenderPartial("InlineEditors/_TextEditor", new TextEditorViewModel
{
PropertyName = nameof(TextWidgetProperties.Text),
Text = Model.Text
});
}
else
{
@Html.Raw(Model.Text);
}
NOTE
The TextWidget widget from the Dancing Goat site is a simple implementation of how to allow the user to enter text. It helps if you set up a sample Dancing Goat MVC site locally and review this code in depth to understand how it functions. Also, know that with MVC, this functionality can be implemented in any number of ways and the sample is just an example.
Testing
With all my custom code in place, I’m ready to add some sweet-widget action to my page. In Part 2, I added an EditableArea to my Home page. Now, when I access the site, I can see my two new widgets are available. You can see it’s using my custom tile and icon, which makes it a much more dynamic experience for the user.
Selecting the MVC Widget List widget adds the new content to the page. The default configuration pulls the three latest widgets and lays them out in a grid. This uses the Partial View we added to display the widgets.
I can configure the widget properties by selecting the cog in the widget interface. You can see it load an interface using the EditingComponent options I defined. These options define how the view will display the data. In my case, I update the settings to pull the top two widgets, which the controller will sort by the Release Date, along with some header and intro text.
Next, I added a Text Widget widget to the page and some sample text. Because this widget uses an Inline Editor, there is no cog to configure it. The user can update the text from directly within the Pages module.
Lastly, I use the MVC Widget List widget on the MVC Widgets page as well. Because this content is populated directly from the content tree, I don’t have to worry about it being searchable and translatable. For the MVC Widgets page, I elect not to use the extra fields and pull all the content items.
Here’s a quick video of the final site with the new widgets added.
Wrapping Up
In this article, I showed you how to add custom widgets to your Kentico MVC sites and display dynamic content. By leveraging the Page Builder and EditableAreas within your MVC views, you allow editors to quickly insert pre-formatted content wherever they need it. This means they can update their content easily without needing a developer involved. Because you created the structure (Model), logic (Controller), and layout (View), you know that the content will always display correctly.
In the next article in the series, I’m going to tackle cleaning and optimizing my MVC code. While the site is not broken, we can use techniques and components to keep concerns separated and follow MVC best practices. This includes adding repositories, Dependency Injection, and View Models. See you next time! And in the meantime, check out our MVC Transition Guide page to get direct access some excellent additional MVC resources to help you kick off your first Kentico MVC project.