-
Notifications
You must be signed in to change notification settings - Fork 68
Create a MVC List widget
This sample demonstrates how to create a simple MVC List widget based on the Feather UI framework. The List widget simply displays a list on the front-end and provides functionality for configuring the list via a customized designer. The goal of the current tutorial is to provide an overview on how to create a MVC widget that use custom designer and that is placed in a separate assembly.
Before you begin make sure you have Feather installed in your project.
- Clone the feather-samples repository.
- Build the ListWidget project.
- Reference the ListWidget.dll from your Sitefinity’s web application.
Project Feather makes it possible to have MVC widgets that are stored in separate assemblies so let’s create the MVC List in a separate assembly. In order to do that you need to perform the following actions:
- Create a new class library named ListWidget.
- Install the Telerik.Sitefinity.Frontend.Core NuGet package.
- Modify the
AssemblyInfo.cs
of the ListWidget by adding the following snippet:
using Telerik.Sitefinity.Frontend.Mvc.Infrastructure.Controllers.Attributes;
[assembly: ControllerContainer]
- Create a MVC folder, MVC/Views folder, MVC/Views/List folder, MVC/Controllers folder, MVC/Controllers/List folder.
- Create a new enumeration named ListMode and add its implementation:
public enum ListMode
{
Numbers,
Bullets
}
Create a new class named ListController
that derives from the System.Web.Mvc.Controller
class and place it in the MVC/Controllers/List folder. Add the following properties to the ListController
class:
//gets or sets the title of list
public string ListTitle { get; set; }
//gets or sets the mode of the list
public ListMode ListType { get; set; }
//gets or sets a serialized list items
public string ListItems { get; set; }
Now let’s create an Index action that passes the title of the list, the type of the list and list items to the Index view of the List widget. For the sake of the simplicity you could use the ViewBag property of the controller class in order to pass the required information to the view. A possible implementation would be as follows:
public ActionResult Index()
{
this.ViewBag.ListTitle = this.ListTitle;
this.ViewBag.ListType = this.ListType;
this.ViewBag.ListItems = this.DeserializeItems();
return this.View();
}
private IList<string> DeserializeItems()
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
IList<string> items = new List<string>();
if (!string.IsNullOrEmpty(this.ListItems))
items = serializer.Deserialize<IList<string>>(this.ListItems);
return items;
}
Last but not least you should add ControllerToolboxItem
attribute to the ListController
class. This way Sitefinity will automatically add the List widget in the toolbox. Here is a sample usage of the ControllerTolboxItem
attribute:
[ControllerToolboxItem(Name = "SampleList", SectionName = "Feather samples", Title = "Sample List")]
At this phase you only need to create an Index view as this is the only view that is used by the ListController
. In order to do that you should create a new Razor view named Index and to place it in the MVC/Views/List folder. Here is a possible implementation of the newly created Index view:
<div class="sfContentBlock">
@{
string openingTag;
string closingTag;
if (@ViewBag.ListType == ListWidget.ListMode.Bullets)
{
openingTag = "<ul>";
closingTag = "</ul>";
}
else
{
openingTag = "<ol>";
closingTag = "</ol>";
}
<h2>
@ViewBag.ListTitle
</h2>
@Html.Raw(openingTag)
foreach (var listItem in ViewBag.ListItems)
{
<li>@listItem</li>
}
@Html.Raw(closingTag)
}
</div>
Feather provides new widget designer created using ASP.NET MVC, Angular and Bootstrap. Once a MVC widget is registered in the toolbox and you press edit, Feather will automatically open for you the new MVC designer. This way you can edit the public properties of the widget though the designer without any additional configuration. Aside from the default property editor view you can add your custom views inside the widget designer in order to show different editing options or more complex UI. The current sample demonstrates how to modify the MVC designer by adding two additional views to it – the “Settings” view and the “Manage items” view. The “Settings” view provides options for customizing the visual appearance of the items on your page, while the “Manage items” view provides simple UI for adding and deleting items of the list.
Customizing the outlook of the MVC designer that is associated with your custom widget does not require any specific configuration, but instead should be done by following a convention. In order to add a custom view to the MVC designer you should follow the below listed rules:
- The name of the custom view file should starts with “DesignerView.”, i.e. DesignerView.[ViewName].cshtml
- The custom view file should be physically located in a Views directory that is related to your widget, i.e. in Mvc/Views/[ControllerName]
- The custom view file should be marked as embedded resource This way Feather will automatically recognize the custom designer view and will generate a button for this view in the widget designer. Behind the scenes Feather creates an Angular controller for your custom designer view and populates its scope with all the properties of the widget that is associated with the MVC designer so inside your custom designer view you can bind html elements to the properties of the widget by simply using the ng-model attribute. For instance, in order to bind an input html element to a property of your widget, you should add the ng-model attribute to the input html element and to set the value of the ng-model attribute to be equal to Properties.[Your property name].PropertyValue. This way you are taking advantage of the two way binding functionality that AngularJS provides.
In order to create a “Settings” view for the List widget designer you should perform the following steps:
- Create a new Razor view named DesignerView.Settings.cshtml.
- Put the newly created file inside Mvc/Views/List.
- Mark the newly created file as embedded resource.
Place the following code in the DesignerView.Settings.cshtm in order to add ability to specify the title of the list widget via the “Settings” view:
<label>List title</label>
<input id="list-title" ng-model="Properties.ListTitle.PropertyValue" type="text" />
Note that you can also easily modify properties of custom types. For instance the following snippet adds UI elements through which one can control the value of the ListType property:
<label class="radio">
<input type="radio" ng-model="Properties.ListType.PropertyValue" value="@ListWidget.ListMode.Numbers">
Numbers
</label>
<label class="radio">
<input type="radio" ng-model="Properties.ListType.PropertyValue" value="@ListWidget.ListMode.Bullets">
Bullets
</label>
If you open the designer of the List widget you will notice that there is a new “Settings” button situated in the right corner of the bottom. You can click on it in order to load the “Settings” view of designer. As a result you’ve just created designer view for editing the properties of your widget without adding even single line of JavaScript.
Sometimes you need to implement a complex UI logic in the widget designer in order to meet specific business requirements. Feather provides an elegant way for building custom views that contain heavy client-side logic. Let’s create a “ManageItems” view that will contain a more complex client-side logic for manipulating the value of the ListItems property of the List widget.
First you need to create a new JavaScript file named manage-items-designer.js and to place it in the Mvc/Scripts folder. Once you create the manage-items-designer.js
file you should define a custom Angular controller in it. By using Angular controllers you have a single place in which you should define the client-side business logic that is associated with your custom designer view. Note that in case you don’t provide a custom Angular controller, the default one will be used.
The custom Angular controller should be named following a specific convention. The name of the controller should be equal to the concatenation of the name of the designer view associated with it and the “Ctrl” suffix. In this particular case you should name the controller ManageItemsCtrl.
You also need to register the ManageItemsCtrl controller in the ‘designer’ module and to specify that it the depends on the '$scope', the 'propertyService' and the 'dialogFeedbackService'. While the ‘$scope’ is an object that refers to the application model and that is part of the Angular API, the ‘propertyService’ and ‘dialogFeedbackService’ are custom Angular services created in Feather that respectively provide functionality for working with the properties of the widget and functionality for showing and hiding the loading indicators and the error messages during service calls.
The first thing that you need to implement in the custom ManageItemsCtrl controller is to retrieve the values of the widget properties and to populate them into the scope. In fact Feather executes exactly the same logic for you behind the scenes for the default designer views. The following code demonstrates how this can be achieved:
$scope.Feedback = dialogFeedbackService;
$scope.Feedback.ShowLoadingIndicator = true;
//Makes call to the controlPropertyService to get the properties for the widgets.
propertyService.get()
.then(function (data) {
if (data) {
$scope.Properties = propertyService.toAssociativeArray(data.Items);
$scope.ListItems = $.parseJSON($scope.Properties.ListItems.PropertyValue);
}
},
function (data) {
$scope.Feedback.ShowError = true;
if (data)
$scope.Feedback.ErrorMessage = data.Detail;
})
.finally(function () {
$scope.Feedback.ShowLoadingIndicator = false;
});
In the scope of the ManageItemsCtrl we added two methods for managing the items:
$scope.newListItem = "";
//Adds item to list.
$scope.AddListItem = function () {
$scope.ListItems.push($scope.newListItem);
$scope.Properties.ListItems.PropertyValue = JSON.stringify($scope.ListItems);
$scope.newListItem = "";
};
//Deletes the selected item from the list.
$scope.DeleteListItem = function (index) {
$scope.ListItems.splice(index, 1);
$scope.Properties.ListItems.PropertyValue = JSON.stringify($scope.ListItems);
};
Notice that in these functions we make sure to update the $scope.Properties.ListItems depending of the action that the user takes. This way when the user clicks the “Save” button Feather will automatically save the updated values of the widget properties. In case user clicks the “Cancel” button Feather will discard the changes applied on $scope.Properties. The last step that you should perform is to register this manage-items-designer.js’. You can register it inside the DesignerView.ManageItems.cshtml by adding the following line:
@Html.Script(Url.WidgetContent("Mvc/Scripts/manage-items-designer.js"))
By default when you open the designer of a widget Feather automatically displays a default designer view. You can modify this behavior through a file named Designer.ClientReferences.cshtml. In general the Designer.ClientReferences.cshtml provides a way for configuring the behavior of the MVC designer for a given widget and also serves as a container for registering the client-side dependencies of the designer such as the required javascript and css files. Let’s specify the “Settings” view as the view that should be displayed when one opens the designer of the List widget. To do that you should create a Designer.ClientReferences.cshtml file, to place it in the Mvc/Views/List folder and specify it as Embedded resource. Note that by creating a Designer.ClientReferences.cshtml file you are overriding the default Designer.ClientReferences.cshtml created by Feather. In this line of thoughts you need to ensure that all dependencies of the MVC designer are registered in the newly created Designer.ClientReferences.cshtml file. To do that you can add the following content in the file:
@using Telerik.Sitefinity.Frontend.Mvc.Helpers
@* Latest compiled and minified CSS `*@
<link rel="stylesheet" href='@Url.WidgetContent("Mvc/Scripts/Bootstrap/css/bootstrap.min.cs`s")'>
@* Optional th_eme *@
<link rel="stylesheet" href='@Url.WidgetContent("Mvc/Styles/sitefinity-theme.mi_n.css")'>
@Html.Script(Url.WidgetContent("Mvc/Scripts/Designer/modal-dialog.js"))
@Html.Script(Url.WidgetContent("Mvc/Scripts/Designer/designer.js"))
@Html.Script(Url.WidgetContent("Mvc/Scripts/Designer/property-grid.js"))
@Html.Script(Url.WidgetContent("Mvc/Scripts/breadcrumb.js"))
@Html.Script(Url.WidgetContent("Mvc/Scripts/manage-it</code></pre>ems-designer.js"))
Once you do that you are ready to specify the default view for the designer by adding the following line:
<input type="hidden" id="defaultView" value=Settings: />
Home | What's new | FAQ