Introduction
This article will help you to build a manageable widget using MVC4 with Razor view. This is not a fully technical article, it will give you a clear concept about the importance of architectural design while building a custom component or feature for our product. Here I will show you the use of a nested partial view that depends on abstraction and also has the extendibility feature.
Using the code
Widgets are very familiar to us. Most sites have this feature with them. It is very important for users to get information in a single view so that s/he can plan their work very easily when logging into the site. So widgets can contain information from many sources. Suppose your company gives you the responsibility to design such a widget module that can be easily plugged with the model and is also dynamic. Here I will discuss the design concept about this type of requirement. Suppose we are building a notice board widget. Here I am using the interface for building that widget. I have two main interfaces on this design:
- IWidget.cs
- ISubWidget.cs
You may ask why two Interfaces, isn't IWidget
enough?
Answer: This is because we need a dynamic widget.
Look at the details here:
public interface ISubWidget
{
string Topic { get; set; }
string Description { get; set; }
}
public interface IWidget
{
int SortOrder { get; set; }
string ClassName { get; set; }
string FooterText { get; set; }
string HeaderText { get; set; }
ISubWidget SubWidget { get; set; }
}
So here IWidget
has a property SubWidget
and its return type is ISubwidget
. Now our IWidget
works like a package for us. So our task is to build to a NoticeBoard Widget, right?
public class NoticeBoard : IWidget
{
public int SortOrder { get; set; }
public string ClassName { get; set; }
public string FooterText { get; set; }
public string HeaderText { get; set; }
public ISubWidget SubWidget { get; set; }
}
public class SubWidget : ISubWidget
{
public string Topic { get; set; }
public string Description { get; set; }
}
Fig 1.1: Dependency Structure of Concrete widgets
We build the widget in classes, now we need to attach it with the MVC application. Before showing the model population I want to show you the design concept for the views. We will use the partial view here. As interfaces we have the same partial view:
- _Widget.cshtml
- _SubWiget.cshtml
Razor view for _Widget.cshtml will populate the IWidget
model and also load the ISubwidget
into its body part. For both of them View/Shared/Widget/ is this path.
@model MvcWidgetBuilder.Models.IWidget
<div class="widget">
<div class="@Model.ClassName">
<div class="header">
@Model.HeaderText
</div>
<div class="body">
@Html.Partial(string.Concat(new[] { "Widget",
"/", "_SubWidget" }), @Model.SubWidget)
</div>
<div class="footer">
@Model.FooterText
</div>
</div>
</div>
The Razor view for _SubWidget.cshtml will populate the ISubWidget
model:
@model MvcWidgetBuilder.Models.ISubWidget
<div class="body">
<div>
<div style="float: left">
@Html.LabelFor(p => p.Topic, @Model.Topic)
</div>
</div>
<div style="clear: both">
@Model.Description
</div>
</div>
So we build our partial views. Who will call them to load them into the browser? We have another view to do this job:
I will just pass the model IWidget
to the partial view _Widget.cshtml. The path of this view is same as before: View/Shared/Widget/.
@model MvcWidgetBuilder.Models.IWidget
@foreach (MvcWidgetBuilder.Models.IWidget wm in ViewBag.Widgets)
{
@Html.Partial(string.Concat(new[] { "Widget", "/", "_Widget" }), wm)
}
Fig. 1.2: Graphical picture of a partial view
So we have three partial views. We will now introduce a view for NoticeBoard.
- NoticeBoard.cshtml (path: View/NoticeBoard/)
- It calls the widgetcontainer
@{
ViewBag.Title = "Widget";
}
@Styles.Render("~/Style/Widget.css")
<h2>Widget</h2>
<p>
@{
@Html.Partial("Widget/_WidgetContainer");
}
</p>
and the controller for NoticeBoard
is NoticeBoardController
. The code for the controller is:
public class NoticeBoardController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult MyNoticeBoard()
{
ViewBag.Widgets = GetWidgetData();
return View();
}
When I call /NoticeBoard/MyNoticeBoard, it just populates ViewBag.Widgets
by calling the below function:
public List<IWidget> GetWidgetData()
{
var noticeboardWidget = new List<IWidget>
{
new NoticeBoard()
{
SortOrder = 1, ClassName = "high",
HeaderText = "Notice Board", FooterText = "" ,
SubWidget = new SubWidget { Topic = "Office Time",
Description = "Office time will be change next month" },
},
new NoticeBoard()
{
SortOrder = 4, ClassName = "medium",
HeaderText = "Notice Board", FooterText = "" ,
SubWidget = new SubWidget { Topic = "Salary",
Description = "Salary is dusburst plese check your account" },
},
new NoticeBoard()
{
SortOrder = 8, ClassName = "low",
HeaderText = "Notice Board", FooterText = "" ,
SubWidget = new SubWidget { Topic = "About Lunch",
Description = "We need the feed back from you about the luch" },
},
new NoticeBoard()
{
SortOrder = 2, ClassName = "high",
HeaderText = "Notice Board", FooterText = "" ,
SubWidget = new SubWidget { Topic = "Emergency Meeting",
Description = "All the managers please come to our meeting room by 11.30" },
},
new NoticeBoard()
{
SortOrder = 5, ClassName = "medium",
HeaderText = "Notice Board", FooterText = "" ,
SubWidget = new SubWidget { Topic = "Office Meetting",
Description = "Todays meeting is cancel" },
},
new NoticeBoard()
{
SortOrder = 7, ClassName = "low", HeaderText = "Notice Board", FooterText = "" ,
SubWidget = new SubWidget { Topic = "HoliDay Notice",
Description = "We shifted our holiday leave for 1 day" },
},
new NoticeBoard()
{
SortOrder = 3, ClassName = "high", HeaderText = "Notice Board", FooterText = "" ,
SubWidget = new SubWidget { Topic = "Vacancy",
Description = "We need a sound asp.net developer with C#" },
},
new NoticeBoard()
{
SortOrder = 6, ClassName = "medium", HeaderText = "Notice Board", FooterText = "" ,
SubWidget = new SubWidget { Topic = "HR Notice",
Description = "Visiting cards proof send to you mail please check" },
},
};
return noticeboardWidget;
}
In browser you will see the below screen:
But if you just change the return by sort order, it just rearranges.
return noticeboardWidget.OrderBy(p=>p.SortOrder).ToList()
Points of Interest
Here my concern is not to show you the CSS or the MVC Razor with the partial view. I just try to focus on the dynamic Widgets design. As we can pass the class names too, we can control the CSS or view by model. Our widget views (partial views) are not dependent on the models (notice board) but on abstraction (IWidget
). So if our company wants to categorise the model such ass HRNotice, SalesNotice, EmployeeNotice, we can easily do that by implementing IWidget
. Those who are beginners can easily feel the importance of the interface because many times I see that people can not even answer the simple question "Why do we need interface?" I hope they find the basic answer from this article.
Another point may come to your mind: What if I want to build a different widget than _Widget
and _SubWidget
? Does our IWidget
support that?
Answer: No, but if we want to make it more dynamic we can do it easily by passing the view name.
public interface ISubWidget
{
string Topic { get; set; }
string Description { get; set; }
string WidgetName { get; set; } <---------------
}
public interface IWidget
{
int SortOrder { get; set; }
string ClassName { get; set; }
string FooterText { get; set; }
string HeaderText { get; set; }
string WidgetName { get; set; } <-------
ISubWidget SubWidget { get; set; }
}
Change on our views as well to load the dynamic view on _WidgetContainer
.
@model MvcWidgetBuilder.Models.IWidget
@foreach (MvcWidgetBuilder.Models.IWidget wm in ViewBag.Widgets)
{
@Html.Partial(string.Concat(new[] { "Widget", "/", wm.WidgetName }), wm)
}
Below code is for our dynamic widget:
@model MvcWidgetBuilder.Models.IWidget
<div class="widget">
<div class="@Model.ClassName">
<div class="header">
@Model.HeaderText
</div>
<div class="body">
@Html.Partial(string.Concat(new[] { "Widget", "/",
@Model.SubWidget.WidgetName }), @Model.SubWidget)
</div>
<div class="footer">
@Model.FooterText
</div>
</div>
</div>
Did you notice how simple it is a change on our design structure makes our models more dynamic. This is possible only because my modules depend on abstraction not on concrete types.