Best practices for implementing custom widgets
Overview
When you develop custom widgets, you need to solve problems such as how to implement canonical URLs, what are the best development practices, or how to reuse built-in widgets logic. This article demonstrates how to use important interfaces or abstract base classes to solve these problems.
ContentBaseController
You use ContentBaseController
abstract class to construct all the out-of-the-box SEO properties for the widget in Detail view mode - when displaying a single item. When you implement a descendant of this class, you can call the InitializeMetadataDetailsViewBag
method and pass the item that is currently being displayed. Based on the item and the SEO configurations of the site, Sitefinity CMS automatically creates the Open Graph and other SEO properties.
GITHUB EXAMPLE: For more information about this class, see the sample code in Sitefinity CMS GitHub repository » ContentBaseController.cs.
ContentModelBase
The ContentModelBase
class holds the auxiliary implementation for various functionality, such as getting content locations, creating generic list view models, creating generic details view models, creating a list view model based on a related data item, part of the implementation of the IHasCacheDependency
interface, and more.
For more information, see ContentModelBase.cs.
IContentLocatableView
You implement the IContentLocatableView
interface, if you want your custom widget to have a canonical URL and to provide a location for items.
For more information, see Register content location with your custom widgets and How to implement IContentLocatableView for custom widgets.
IHasCacheDependency
You implement IHasCacheDependency
interface to provide cache invalidation for the respective content type in your custom widget. If this interface is not implemented, there can be stale content displayed in the following scenario:
- The user that is logged in is not a backend user or no user is logged in.
Sitefinity CMS does not use cache when serving content to backend users.
- The widget is displayed on a page and the user has requested this page, thus, the query made by the widget is cached according to the cache profile.
- An editor makes a change to an item that the widget is displaying.
- The user will not see the change until the cache expires, since there is no way for Sitefinity CMS to know that these items are being displayed by this widget.
For more information, see Implement Cache Dependencies.
IRouteMapper
You implement IRouteMapper
interface when you need custom logic for resolving the route parameters. When you implement it, the default behavior for MVC routing will be turned off and only your custom routing implementation will be used.
EXAMPLE: You have custom book widget that displays a list of books on a page. You can implement custom routing logic for filtering by a specific author that will resolve the required author from the route – for example, http://mysite/book-catalog/Index/Author. For more information, see #region IRouteMapper
in the sample at the end of the article.
HandleUnknownAction
You use the HandleUnknownAction
method in a Sitefinity CMS widgets to retain a correct state in certain scenarios. One such scenario is the details mode of a separate widget. When a page is rendered with a widget in a mode different than the default one, Sitefinity CMS checks all the widgets on the page whether they can go into this mode. In this case, the news widget is going into details mode and Sitefinity CMS is trying to push all widgets in this mode. The widgets who have details action will get mapped earlier and Sitefinity CMS will not execute them. However, the widgets that do not have details action must implement the HandleUnknownAction
method so that the developer can specify what action should be executed in such a case.
For more information see HandleUnknownAction
in the sample at the end of the article.
Returning a view
When you return a view in your Controller class, always name explicitly the view that is going to be called:
return this.View("ViewName", viewModel);
For more information, see the sample below.
Code sample
The following sample demonstrates how to use IContentLocatableView
, IRouteMapper
, HandleUnknownAction
, and how to return a view from your Controller
classes: