Objective
The objective of the article is to create an Event Calendar for a community website which is the most commonly used feature in any web site these days. The web page will display events for current and future years. When each event is clicked, the details would be displayed in a pop up box .The web application will concentrate on creating loosely coupled code and show how to code without making use of code behind files using the following concepts –HTTP Handlers, Web Services, MVP, JavaScript and AJAX.
Introduction
When developing an ASP.NET web application, we all often make use of the property of code behind files available to us. This probably works well for small and less complex websites where everything is in .NET and things are unlikely to change much over time. But when it comes to scalability and interoperability with other platforms, it really is not a good idea.
Example – You have an ASP.NET web application which works great. Suddenly the client decides to shift the UI code from aspx pages to simple HTML pages. A lot of code in the code behind simply makes the application closely coupled and makes the transition very difficult. Also, we should actually maintain separation of concerns. The UI should be handled by the designers and the business logic should be left to the developers. Writing a lot of code in the code behind files actually blurs the line separating designers and developers. The best way would be to segregate design from business logic so that we would have great aesthetic websites with rich UI interface (created by designers) rendering complex business logic (by developers) and both coexisting in perfect harmony.
The Project
The project is divided into two parts:
- Part 1 – Server
- Part 2 – Client
Part 1 – The Server
The server consists of the following parts:
- Database – Tables, Stored Procedures
- Data Access Layer
- Business Layer
- Web Service
The Database
I have used a SQL Server database to host the events for the event calendar. Please refer to the project named EventCalendar.SQLDatabase
. That contains necessary scripts for creating the database. Basically I have events for the calendar year – 2010, 2011 and 2012. I have filled the database with dummy data. Let’s have a quick look at our database structure.
A picture is better than a thousand words. I will attach screen shots of my database design and database diagram. In a nutshell, we are storing events for each month for various calendar years.
Database Tables
Table Events
Table Year
Table Month
Database Diagram
Note
- I have taken a backup of my database and attached as downloadable with this project. You can download the .bak file and run a restore. That should work.
- You can also use the scripts provided to create your own database. My database is SQL Server 2008. You have to make adjustments if you are using other versions.
- For real time scenarios, you would need to take a call on whether you want a Relational Database or XML. If you have a small website with a few events per year, it would make sense to use XML instead.
Database Stored Procedures
There are two stored procedures defined:
Database Snapshot
Data Access Layer
Refer project – EventCalendar.DataAccessLayer
I have used Factory Database provider to implement our data access layer. I have an IDatabaseConnection
interface which defines our database connection. It has three methods:
GetConnection
– To create connection
CreateCommand
– To create a command object
CreateAdapter
– To create a database adapter
public interface IDatabaseConnection:IDisposable
{
IDbConnection GetConnection { get; }
IDbCommand CreateCommand(string dynamicSQLExpression , CommandTypes cmdType);
IDbDataAdapter CreateAdapter(IDbCommand command);
}
public enum CommandTypes
{
StoredProcedure,
Query
}
We have a DatabaseConnection
class which implements our IDatabaseConnection
:
public class DatabaseConnection:IDatabaseConnection
The main advantage of using a DbProviderFactory
is to remove any coupling from the underlying database. Our code should be compatible with any database. This is achieved by getting the provider name at run time from a config file.
private string _providerName =
ConfigurationManager.AppSettings["DataAccessLayer.ProviderName"];
private string _connectionString =
ConfigurationManager.AppSettings["DataAccessLayer.ConnectionString"];
private DbProviderFactory _providerFactory;
private IDbConnection _connection;
We use the GetFactory
method of the DBProviderFactory
class to create a provider factory and then open database connection in the constructor of the class. See the code below:
public DatabaseConnection()
{
_providerFactory = DbProviderFactories.GetFactory(_providerName);
OpenConnection();
}
private void OpenConnection()
{
_connection = _providerFactory.CreateConnection();
_connection.ConnectionString = _connectionString;
_connection.Open();
}
private void CloseConnection()
{
_connection.Close();
}
System.Data.IDbConnection IDatabaseConnection.GetConnection
{
get { return _connection; }
}
Finally we implement the CreateCommand
, CreateAdapter
methods:
System.Data.IDbCommand IDatabaseConnection.CreateCommand
(string dynamicSQLExpression, CommandTypes cmdType)
{
IDbCommand command = _connection.CreateCommand();
command.CommandText = dynamicSQLExpression;
switch (cmdType)
{
case CommandTypes.StoredProcedure:
command.CommandType = CommandType.StoredProcedure;
break;
case CommandTypes.Query:
command.CommandType = CommandType.Text;
break;
default:
break;
}
return command;
}
IDbDataAdapter IDatabaseConnection.CreateAdapter(IDbCommand command)
{
IDbDataAdapter da = _providerFactory.CreateDataAdapter();
da.SelectCommand = command;
return da;
}
void IDisposable.Dispose()
{
CloseConnection();
_connection.Dispose();
}
We have now created our provider factory which can return us the necessary command and adapter objects to access our database. So, we must now create a DatabaseGateway
which will use our DatbaseConnection
class to fetch data from the database.
The DatbaseGateway
has two methods:
ExecuteQuery
– For executing queries. It has two overloads – one for running a normal query and another for a parameterized query.
ExecuteStoredProcedure
– For executing stored procedures. It has two overloads – one for running a normal SP and another for a parameterized SP.
The parameters are passed as hashtable collection as arguments to the methods.
See the code below:
public class DatabaseGateway
{
public DatabaseGateway() {}
public DataTable ExecuteQuery(string SQlQuery)
{
using (IDatabaseConnection conn = new DatabaseConnection())
{
DataSet ds = new DataSet();
conn.CreateAdapter(conn.CreateCommand
(SQlQuery, CommandTypes.Query)).Fill(ds);
return ds.Tables[0];
}
}
public DataTable ExecuteQuery(string SQlQuery, Hashtable param)
{
using (IDatabaseConnection conn = new DatabaseConnection())
{
DataSet ds = new DataSet();
IDbCommand cmd = conn.CreateCommand(SQlQuery, CommandTypes.Query);
foreach (DictionaryEntry item in param)
{
IDataParameter p = cmd.CreateParameter();
p.ParameterName = (string)item.Key;
p.Value = item.Value;
cmd.Parameters.Add(p);
}
conn.CreateAdapter(cmd).Fill(ds);
return ds.Tables[0];
}
}
public DataTable ExecuteStoredProcedure(string SQlQuery)
{
using (IDatabaseConnection conn = new DatabaseConnection())
{
DataSet ds = new DataSet();
conn.CreateAdapter(conn.CreateCommand
(SQlQuery, CommandTypes.StoredProcedure)).Fill(ds);
return ds.Tables[0];
}
}
public DataTable ExecuteStoredProcedure(string SQlQuery, Hashtable param)
{
using (IDatabaseConnection conn = new DatabaseConnection())
{
DataSet ds = new DataSet();
IDbCommand cmd = conn.CreateCommand
(SQlQuery, CommandTypes.StoredProcedure);
foreach (DictionaryEntry item in param)
{
IDataParameter p = cmd.CreateParameter();
p.ParameterName = (string)item.Key;
p.Value = item.Value;
cmd.Parameters.Add(p);
}
conn.CreateAdapter(cmd).Fill(ds);
return ds.Tables[0];
}
}
}
Business Layer
Now that we have created our Data Access Layer, it is time to create our Business Layer. Class Event – This represents our events in the event calendar.
public class Event
{
public Event() {}
public string Date { get; set; }
public string Description { get; set; }
public string Link { get; set; }
public string Details { get; set; }
public string Image { get; set; }
}
Class Month – This represents months for the years. So each month has the following properties:
MonthID
– Represents which month of the year
Year
– Represents which year the month belongs to
Events
– Collection of events for that month. Refer to the code below:
public class Month
{
public Month() { }
public Month(byte monthID)
{
this.MonthID = monthID;
}
public byte MonthID { get; set; }
public string MonthName { get; set; }
public string Year { get; set; }
public List<Event> Events
{
get
{
DatabaseGateway dal = new DatabaseGateway();
Hashtable parameters = new Hashtable();
parameters.Add("@Year", Year);
DataTable dtYear=dal.ExecuteQuery
("SELECT YearId FROM YEAR WHERE Year = @Year",parameters);
int YearId = Convert.ToInt16(dtYear.Rows[0][0]);
string sqlQuery = ConfigurationManager.AppSettings
["DataAccessLayer.spEventQuery"];
dal = new DatabaseGateway();
parameters = new Hashtable();
parameters.Add("@yearId", YearId);
parameters.Add("@monthId", MonthID);
DataTable dtEvents = dal.ExecuteStoredProcedure(sqlQuery,parameters);
List<Event> events = new List<Event>();
for (int rowCount = 0; rowCount < dtEvents.Rows.Count; rowCount++)
{
Event e = new Event();
e.Date = Convert.ToString(dtEvents.Rows[rowCount]["Date"]);
e.Description = Convert.ToString(dtEvents.Rows
[rowCount]["Description"]);
e.Link =Convert.ToString(dtEvents.Rows[rowCount]["Link"]);
events.Add(e);
}
return events;
}
}
}
Class Calendar
– This represents each calendar year. It has the following properties:
Year
– Represents the Year
Months
– Collection of months (Jan – Dec) - Refer to the code below:
public class Calendar
{
public Calendar(){}
public string Year { get; set; }
public List<Month> Months
{
get
{
string sqlQuery = ConfigurationManager.AppSettings
["DataAccessLayer.spMonthQuery"];
DatabaseGateway dal = new DatabaseGateway();
DataTable dtMonths = dal.ExecuteStoredProcedure(sqlQuery);
List<Month> months = new List<Month>();
for (int rowCount = 0; rowCount < dtMonths.Rows.Count; rowCount++)
{
Month m = new Month();
m.MonthID = Convert.ToByte(dtMonths.Rows[rowCount]["MonthId"]);
m.MonthName = Convert.ToString(dtMonths.Rows[rowCount]["Month"]);
m.Year = Year;
months.Add(m);
}
return months;
}
}
}
Note that I have called the database gateway class from the Data Access Layer to fetch data from the database. This has been implemented in the properties of the respective classes.
The Model View Presenter
The MVP pattern gives immense flexibility when augmenting Business Layer (Model) to my presenter (UI). The main advantage of using the MVP pattern is that it makes your code loosely coupled with the actual UI by shifting all the code from the code behind files to the Presenter. Hence, when there is a change in the UI, we would have minimum or no breaking change at all.
Model – This represents the Business Layer and the Data Access Layer.
View Abstraction – We were talking about separation of concerns between designers and developers at the beginning of the article. Imagine that you are working with a set of designers to deliver a rich user interface website. As a developer, you would work on the business logic. Now, if your business logic is closely coupled with your UI, you would be at the mercy of the designers to develop and test your code. Also, it hampers parallel programming because developers and designers are totally dependant on each other and would often step over each other’s toes. Enter Model View Pattern! Using MVP, we do not actually need a view but just an abstraction of the view. We can write our own test cases to test the functionality when the actual view is being developed simultaneously.
Presenter – The presenter has an association with the view and is responsible for updating the view by fetching data from the Model.
As we are dealing with an abstraction of the view, the functionality of the view can be easily tested without the actual view (our rich UI). All we have to do is define a class in our test module which implements the abstraction and make it our view. We can then test our business logic and when the actual view is ready, our code would fit in perfectly. Plug and Play!
View – This is the actual view or UI. This implements the abstracted view.
Let's see how we can make use of MVP in our project. Refer to EventCalendar.Presenter
. We have two presenters because we are dealing with two views.
- The
Event
calendar which displays events for various years.
- The pop up box that shows up displaying the detailed event when an event header is clicked.
View Abstraction – IeventCalendarView
public interface IEventCalendarView
{
string SelectedYear { get; }
string HTMLCode { set; }
}
The HTML code here refers to the HTML code that will be returned by the presenter to the view. This HTML code will have the inner HTML code for the event calendar web page.
Presenter – EventCalendarPresenter
.
The Presenter here communicates with the Model (our Data Access Layer and Business Layer) to fetch event information. It then generates a HTML code that the actual view would implement. The reason I chose this design is to keep up with the styling of the web page. Usually, the designers will generate HTML codes to decorate the webpage. It is easier to just write information in the inner HTML of any control. Here I would be writing it in the <div>
section of the actual view.
Anyone willing to implement any other design would just have to change the code related to the HTMLCode
property of the view.
We have a JavaScript function at the client side called pop up, which sends request to our DetailedEvent
web service. This function (explained later) is responsible for showing the detailed event in the pop up.
IEventCalendarView _view;
public EventCalendarPresenter(IEventCalendarView view)
{
_view = view;
}
public void UpdateView()
{
Calendar c = new Calendar();
c.Year = _view.SelectedYear;
_view.HTMLCode = GenerateHTMLCodeForaCalendar(c);
}
#region InnerHTML Code
private string GenerateHTMLCodeForaCalendar(Calendar c)
{
StringBuilder innerHTML = new StringBuilder();
innerHTML.Append("<table border=\"0\"
cellspacing=\"0\" cellpadding=\"0\" class=\"tableEvents\">");
byte monthCount = 1;
ArrayList arrMonths = new ArrayList();
for (int monthId = 0; monthId < 12; monthId++)
{
Month month = c.Months.Find((m) => { return m.MonthID == monthId + 1; });
arrMonths.Add("<th>" + month.MonthName + "</th><td></td>");
}
for (int row = 0; row < 3; row++)
{
if (row == 0)
{
innerHTML.Append("<tr class=\"head\">");
innerHTML.Append(arrMonths[0]);
innerHTML.Append(arrMonths[1]);
innerHTML.Append(arrMonths[2]);
innerHTML.Append(arrMonths[3]);
innerHTML.Append("</tr>");
}
else if (row == 1)
{
innerHTML.Append("<tr class=\"head\">");
innerHTML.Append(arrMonths[4]); innerHTML.Append(arrMonths[5]);
innerHTML.Append(arrMonths[6]); innerHTML.Append(arrMonths[7]);
innerHTML.Append("</tr>");
}
else
{
innerHTML.Append("<tr class=\"head\">");
innerHTML.Append(arrMonths[8]); innerHTML.Append(arrMonths[9]);
innerHTML.Append(arrMonths[10]); innerHTML.Append(arrMonths[11]);
innerHTML.Append("</tr>");
}
innerHTML.Append("<tr>");
for (int col = 0; col < 4; col++)
{
innerHTML.Append("<td class=\"eventCont\">
<div class=\"scroll-pane\"><div>");
Month month = c.Months.Find((m) =>
{ return (m.MonthID == monthCount); });
innerHTML.Append(EventsInaMonth(month));
innerHTML.Append("</div></div></td><td> </td>");
monthCount += 1;
}
innerHTML.Append("</tr>");
}
innerHTML.Append("</table>");
return innerHTML.ToString();
}
private string EventsInaMonth(Month month)
{
StringBuilder text = new StringBuilder();
text.Append("<table width=\"100%\" border=\"0\"
cellspacing=\"0\" cellpadding=\"0\">");
foreach (Event e in month.Events)
{
text.Append("<tr>");
text.Append("<td><strong>");
text.Append("<a href=\"#");
if (e.Description != "")
text.Append("\" onclick=\"popup('event=" + e.Date +
"&month=" + month.MonthID + "&year=" + month.Year + "')\"> ");
else
text.Append("\" onclick=\"popup('month=" +
month.MonthID + "&year=" + month.Year + "')\"> ");
text.Append(e.Date);
text.Append("</a>");
text.Append("</strong>");
text.Append("<p>");
text.Append(e.Description);
text.Append("</p></td></tr>");
}
text.Append("</table>");
return text.ToString();
}
#endregion
}
View IDetailedEventView
Similarly, we have the Detailed Event view. It has the following properties:
public interface IDetailedEventView
{
string SelectedYear { get; }
string JSONstring { set; }
string date { get; }
int monthid { get; }
}
The DetailedEventPresenter
is responsible for fetching the detailed description of the event and serialize it in the form of JSON string
and attach it to the view.
public class DetailedEventPresenter
{
private IDetailedEventView _view;
List<Month> _months;
public DetailedEventPresenter(IDetailedEventView view)
{
_view = view;
_months = new List<Month>();
}
public void UpdateView()
{
int intCurrentYear = DateTime.Now.Year;
Calendar c = new Calendar();
c.Year = _view.SelectedYear;
_months = c.Months;
Month month = _months.Find((m) => { return (m.MonthID == _view.monthid); });
Event selectedEvent = month.Events.Find((e) =>
{ return (e.Date == _view.date); });
string jsonString = "{'title':'" +
selectedEvent.Description + "', 'date':'" + selectedEvent.Date +
"', 'details':'" + selectedEvent.Details + "',
'link':'" + selectedEvent.Link + "', 'image':'" +
selectedEvent.Image + "'}";
_view.JSONstring = jsonString;
}
}
Note – The presenters have a method – UpdateView()
which it calls to update the actual view with data from the model.
Testing our Code – At this point, we can actually implement our unit test cases to test if our functionality works. Note that we do not have an actual implementation of the view as yet. We can use the test projects in Visual Studio. All we have to do is implement a class which inherits the view abstraction – Dummy View – and call the presenter to see if our business logic works.
I have not included much testing due to lack of time, but readers are welcome to add their own.
The WebService
There are two ways of implementing the web service:
- Using simple aspx pages (Project –
EventCalendar.UI
)
- Using HTTP Handlers (Project –
EventCalendar.WebService
)
The idea is to make the information available to any client subscribing to our web service.
Web Service using Simple ASPX Pages
The idea is to write the information on a web page. This gives us simplicity as we do not have to host it in IIS separately. It can be deployed with our client as is.
Events.aspx – This is our view for the Event Calendar. We implement the abstract view and associate our presenter with it. The web page expects information in the form of query string. We capture that information to understand which events to be fetched based on year and month passed. We then call the UpdateView()
method of the presenter to fill the page with information.
public partial class Events : System.Web.UI.Page, IEventCalendarView
{
private EventCalendarPresenter presenter;
private string _selectedYear;
protected void Page_Load(object sender, EventArgs e)
{
presenter = new EventCalendarPresenter(this);
_selectedYear = Request["year"];
if (_selectedYear==null)
{
_selectedYear = Convert.ToString(DateTime.Now.Year);
}
presenter.UpdateView();
}
string IEventCalendarView.SelectedYear
{
get { return _selectedYear; }
}
string IEventCalendarView.HTMLCode
{
set { Response.Write(value); }
}
}
DetailedEvent.aspx page – This becomes our View for detailed event display. We implement the abstract view and associate our presenter with it. The web page expects information in the form of query string
. We capture that information to understand which events to be fetched based on year and month passed. We then call the UpdateView()
method of the presenter to fill the page with information.
public partial class DetailedEvent : System.Web.UI.Page, IDetailedEventView
{
private DetailedEventPresenter presenter;
private string _selectedYear;
private string _event;
private string _month;
protected void Page_Load(object sender, EventArgs e)
{
_month = Request["month"];
_event = Request["event"];
presenter = new DetailedEventPresenter(this);
_selectedYear = Request["year"];
if (_selectedYear == null)
{ _selectedYear = Convert.ToString(DateTime.Now.Year);}
presenter.UpdateView();
}
string IDetailedEventView.SelectedYear { get { return _selectedYear; }}
string IDetailedEventView.JSONstring { set { Response.Write(value); }}
string IDetailedEventView.date { get { return _event; }}
int IDetailedEventView.monthid
{ get { return Convert.ToInt16(_month); }}
}
Note that we have significantly reduced the amount of code in the code behind files by implementing the MVP pattern. Thus the bulk of the operation here is being handled by the presenter.
Web Service using HTTP Handlers
With all the simplicity of the above code, it is not elegant. By calling aspx pages for just spitting out information, we are using up memory in the server. It should be better left with HTTP handlers who are killers for this kind of job.
We again have two views as above. The implementation is the same. Refer to the code below:
public class DetailedEvent:IHttpHandler,IDetailedEventView
{
private DetailedEventPresenter _presenter;
private string _selectedYear;
private string _event;
private string _month;
private HttpContext _context;
bool IHttpHandler.IsReusable
{
get { throw new NotImplementedException(); }
}
void IHttpHandler.ProcessRequest(HttpContext context)
{
_context = context;
_month = context.Request["month"];
_event = context.Request["event"];
_presenter = new DetailedEventPresenter(this);
_selectedYear = context.Request["year"];
if (_selectedYear == null) { _selectedYear =
Convert.ToString(DateTime.Now.Year); }
_presenter.UpdateView();
}
string IDetailedEventView.SelectedYear { get { return _selectedYear; } }
string IDetailedEventView.JSONstring
{ set { _context.Response.Write(value); } }
string IDetailedEventView.date { get { return _event; } }
int IDetailedEventView.monthid
{ get { return Convert.ToInt16(_month); } }
}
public class Events:IHttpHandler,IEventCalendarView
{
private string _selectedYear;
private EventCalendarPresenter _presenter;
private HttpContext _context;
public Events()
{
_presenter = new EventCalendarPresenter(this);
}
bool IHttpHandler.IsReusable
{
get { throw new NotImplementedException(); }
}
void IHttpHandler.ProcessRequest(HttpContext context)
{
EventCalendar?year=selectedYear
_context = context;
_presenter = new EventCalendarPresenter(this);
_selectedYear = context.Request["year"];
if (_selectedYear == null)
{
_selectedYear = Convert.ToString(DateTime.Now.Year);
}
_presenter.UpdateView();
}
string IEventCalendarView.SelectedYear
{
get
{ return _selectedYear; }
}
string IEventCalendarView.HTMLCode
{
set
{ _context.Response.Write(value); }
}
}
Note – If you are using the HTTP Handlers, you have to host them separately in IIS and configure the virtual directory and the Web.config files. To learn more about HTTP Handlers, please see the reference section.
The Web.Config File
In addition to the above code, you have to also update the web.config file as below:
<appSettings>
<add key="DataAccessLayer.ProviderName" value="System.Data.SqlClient"/>
<add key="DataAccessLayer.ConnectionString"
value="Data Source=SYNPUNEHCRV-127;
Initial Catalog=EventCalendar;User ID=sa;Password=SA!@#"/>
<add key="DataAccessLayer.spEventQuery" value="spGetEvents"/>
<add key="DataAccessLayer.spMonthQuery" value="spGetMonths"/>
</appSettings>
Part 2 – The Client
The client is a HTML webpage interacting with the server by subscribing to its web services. The client sends out an XML HTTP request through AJAX. It receives the response, desterilizes it if needed and displays the information. All this happens through Java script functions written in separate script files.
Refer to the project EventCalendar.UI
.
The client has the following parts:
- CSS - We have our style sheets in the CSS folders. They are self-explanatory. In real time situations, these will be provided by the designers.
- Images – Contain the images for our UI
- JS – Contains the necessary script files. These JavaScript files are responsible for the following tasks:
- Send XML HTTP request to the web service
- Implement the tab control
- Handle events on tab click
- Implement the lightbox or pop up window
It is worthwhile to explain the script.js file at this point.
The onload
function is called when the page gets loaded. As explained earlier, the page sends out XML HTTP requests to the web services:
var e, i = 0, responseCurrent, responseNext, responseFuture;
if (document.getElementById('tabs') != null) {
var AJAX = null;
if (window.XMLHttpRequest) {
AJAX = new XMLHttpRequest();
} else {
AJAX = new ActiveXObject("Microsoft.XMLHTTP");
}
if (AJAX) {
if (responseCurrent == null) {
AJAX.open("GET", "Events.aspx?year=2010", false);
AJAX.send(null);
responseCurrent = AJAX.responseText;
}
if (responseNext == null) {
AJAX.open("GET", "Events.aspx?year=2011", false);
AJAX.send(null);
responseNext = AJAX.responseText;
}
if (responseFuture == null) {
AJAX.open("GET", "Events.aspx?year=2012", false);
AJAX.send(null);
responseFuture = AJAX.responseText;
}
It then populates the inner HTML section of the div
“Constable
” with the information returned by the web service.
var div = document.getElementById('Constable');
div.innerHTML = responseCurrent;
The below section handles the click events of the tabs. It again loads the innerHTML
of the div
with HTML code returned by the web service based on the year clicked.
while (e = document.getElementById('tabs').getElementsByTagName('li')[i++]) {
if (e.className == 'on' || e.className == 'off') {
e.onclick = function() {
var getEls =
document.getElementById('tabs').getElementsByTagName('li');
for (var z = 0; z < getEls.length; z++) {
var div =
document.getElementById(getEls[z].getAttribute('title'));
if (div.id == 'Constable') {
div.innerHTML = responseCurrent;
}
else if (div.id == 'Monet') {
div.innerHTML = responseNext;
}
else if (div.id == 'Vincent') {
div.innerHTML = responseFuture;
}
div.className = div.className.replace('show', 'hide');
getEls[z].className = getEls[z].className.replace('on', 'off');
}
this.className = 'on';
var max = this.getAttribute('title');
document.getElementById(max).className = "show";
}
}
I have used JQuery to implement the pop up window. It is basically a lightbox
control. Refer to the files – Jquery.js and jquery.simplemodal.js.
The below function implements the pop up. It again sends a XML HTTP request to fetch the detailed event information from the web service and injects the code in the inner HTML section of the div
. The jquery is programmed to display the div
on click of an event header.
Note that the events that are being populated are being fetched by calling the web page Events.aspx. We discussed earlier how the inner HTML code was generated by the presenter of that web page to achieve this functionality:
function popup(id) {
id = "DetailedEvent.aspx?" + id;
var AJAX = null;
if (window.XMLHttpRequest) {
AJAX = new XMLHttpRequest();
} else {
AJAX = new ActiveXObject("Microsoft.XMLHTTP");
}
if (AJAX) {
AJAX.open("GET", id, false);
AJAX.send(null);
response = AJAX.responseText;
eval("json = " + response + ";");
}
var divImage = document.getElementById("imgDiv");
divImage.innerHTML = '<img src="' + json.image + '" alt="" />';
var divheadCont = document.getElementById("headCont");
divheadCont.innerHTML = '<h2>' + json.date + '</h2><h3>' + json.title + '</h3>';
var divrow2 = document.getElementById("row2");
divrow2.innerHTML = '<p>' + json.description + '</p>';
var diveventMainWrap = document.getElementById("eventMainWrap");
$("#eventView").modal();
}
Finally, we have the client HTML page - upcoming-events.html that implements necessary styling.
Summary
We can summarize the project with the below diagram – Presenter View (Web service).
This concludes our project. We have successfully implemented a web based event calendar using MVP pattern and client – server architecture. The project demonstrates how to achieve separation of concerns between design and development. The project also explains how to remove dependency on code behind files and develop .NET applications so that they can be easily interoperable amongst various platforms. In this case, we have a simple HTML client as our UI which renders complex business logic developed using .NET. This can easily be shifted to any other platform like – Java or PHP as along as they can make AJAX calls to the web services.
References
For details on HTTP Handlers, please see the following article: