Note: Check out the working portal at http://aspspider.info/lakkakula/local.aspx.
In this series:
1. Introduction
2. Part 1 – Portal Core Functionality
3. Part 2 – Drag and Drop Widget Personalization
4. Part 3 – Building Widget Gallery
5. Part 4 – Introduce Tabs and Users
6. Part 5 – Enhancements and Conclusion
Where do we start? Well, I prefer to start with database. Portal database-design itself is very simple. We need to have containers for Widgets (basic building blocks of the portal), Tabs (logical grouping of Widgets) and Users (so that we can assign Widgets to users through tabs). Below is a simple relational diagram between these entities.
Each Widget is derived from WidgetBase and is associated to Tab. Users will have access to one or more Tabs.

This design is self explanatory, we will use WidgetBase table to store and display widget gallery something similar like this: (courtesy netvibes.com). We will implement this some time later

Once we have the database ready, its time to build core functionality of the portal, which is “to display widgets from list of widgets on a web page”. Let us just do that right here. We will then introduce widget gallery, tabs and finally users.
This is what we will do here. First; get the list of widgets in JSON format to client, second; design a widget client template and finally bind JSON to widget client template.
Getting the list of widgets in JSON format to client:
var widgets = [];
$(function() {
$.getJSON('Local/WidgetList', null, function(json) {
Sys.Observer.addRange(widgets, json);
});
});
When the page is loaded first time, above code makes a call to WidgetList action method inside Local controller. Once the call completes, the resulting JSON will be assigned to widgets[]array.
Below is the sample JSON returned to client
{ id = 4, col = "col1", order = 1, domain = "http://yelp.com", title = "Local Reviews", minimize = false, showsetup = true, controller = "Reviews", action = "Index", footerlink = "footer-link" ... }
{ id = 5, col = "col1", order = 2, domain = "http://wikihow.com", title = "Wiki - How To", minimize = false, showsetup = true, controller = "Feed", action = "Index", footerlink = "footer-link" ... }
{ id = 6, col = "col1", order = 3, domain = "http://cnn.com", title = "CNN.com", minimize = false, showsetup = true, controller = "Feed", action = "Index", footerlink = "footer-link" ... }
{ id = 7, col = "col2", order = 1, domain = "http://yahooapis.com", title = "Weather", minimize = false, showsetup = true, controller = "Feed", action = "Index", footerlink = "footer-link" ... }
{ id = 11, col = "col2", order = 2, domain = "http://flickr.com", title = "Flickr", minimize = false, showsetup = true, controller = "Flickr", action = "Index", footerlink = "footer-link" ... }
{ id = 8, col = "col2", order = 3, domain = "http://bbc.co.uk", title = "BBC News", minimize = false, showsetup = true, controller = "Feed", action = "Index", footerlink = "footer-link" ... }
{ id = 10, col = "col3", order = 1, domain = "http://topix.com", title = "Topix", minimize = false, showsetup = true, controller = "Feed", action = "Index", footerlink = "footer-link" ... }
{ id = 9, col = "col3", order = 2, domain = "http://google.com", title = "Google News", minimize = false, showsetup = true, controller = "Feed", action = "Index", footerlink = "footer-link" ... }
public class LocalController : Controller {
IWidget repoWidget;
// Dependency Injection enabled constructors
public LocalController() : this(new WidgetRepository()) { }
public LocalController(IWidget repo) {
repoWidget = repo;
}
public JsonResult WidgetList(){
var wiz = repoWidget.WidgetList();
return Json(wiz);
}
}
Only interesting thing to note here is that we are returning the results in JSON format as the action method WidgetList() is declared to return JsonResult. The line
var wiz = repoWidget.WidgetList();
returns a list of widgets. And then next line
return Json(wiz);
converts the results to JSON format which then be sent to client.
Designing widget client template:
This actually is a widget container template, original widget will be returned by server through an ajax call. This is explained below on how to do it through declarative jQuery.
<div id="widget-template" class="sys-template">
<div code:if="col==$element.id" id="{{'widget-' + id}}" class="widget">
<div class="widget-header">
<div class="widget-controls hide-controls">
<button class="widget-ctrl ctrl-setup" type="button"></button>
<button class="widget-ctrl ctrl-refresh" type="button" onclick="{{'return $.ajaxUtil.navigate(this, \'widget-content\',\'' + controller + '/' + action + '/' + id + '\');'}}"></button>
<button class="widget-ctrl ctrl-collapse" type="button" onclick="linksgrid.toggleWidget(this);"></button>
<button class="widget-ctrl ctrl-close" type="button" onclick="linksgrid.removeWidget(this)"></button>
</div>
<a sys:href="{{domain}}" target="_blank"><img sys:src="{{domain + '/favicon.ico'}}" style="height:16px; width:16px; padding-right:5px; vertical-align:middle; float:left;" /></a>
<h2>{{title}}</h2>
<div class="wiz-head-sep"></div>
</div>
<div class="widget-body" id="{{'widtet' + id + 'body'}}">
<div id="{{'widget-' + id + '-content'}}" class="widget-content" sys:attach="ajaxload" ajaxload:url="{{'' + controller + '/' + action + '/' + id}}">
<div style="text-align: center; padding: 20px; margin: 5px;"><img src="/Content/images/loading.gif" alt="loading widget" /></div>
</div>
</div>
<div class="widget-footer" id="{{'widget-' + id + '-footer'}}">
<a href="#">{{footerlink}}</a>
</div>
</div>
</div>
Widget Container:

Points to note in above Client Template are
1. the line here <div id=”{{‘widget-‘ + id + ‘-content’}}” class=”widget-content” sys:attach=”ajaxload” ajaxload:url=”{{” + controller + ‘/’ + action + ‘/’ + id}}”> is of special interest. This line of code is using declarative way of specifying jQuery load method. You can read about this on Brian J. Cardiff’s blog. And also on Bertrand Le Roy’s blog. Bertrand is the brains behind Microsoft Ajax Client Templates, I suggest following his blog for up-to-date news.
The idea here is to make an ajax (get) call to specified action in the specified controller with any query parameters. Once the call is made; the resulting html will be placed inside widget container.
Here is the declarative jQuery code for your reference:
/*
* jQuery Declarative plug-in 0.1 (3/12/2008)
*
* Copyright (c) 2008
* Brian J. Cardiff – http://weblogs.manas.com.ar/bcardiff/
*
* Built upon
* jQuery 1.2.6 (http://jquery.com)
* Microsoft Ajax (MicrosoftAjax.js)
* Microsoft Ajax Templates (MicrosoftAjaxTemplates.js)
*
*/
(function($) {
Type.registerNamespace(“jQueryDeclarative”);
var template = “jQueryDeclarative.{0} = function(element) {{ jQueryDeclarative.{0}.initializeBase(this, [element]); }}; jQueryDeclarative.{0}.prototype = {{ initialize: function() {{ jQueryDeclarative.{0}.callBaseMethod(this, ‘initialize’); jQuery(this.get_element()).{0}(this); }}, dispose: function() {{ jQueryDeclarative.{0}.callBaseMethod(this, ‘dispose’); }} }}; jQueryDeclarative.{0}.registerClass(‘jQueryDeclarative.{0}’, Sys.UI.Behavior); if (typeof (Sys) !== ‘undefined’) Sys.Application.notifyScriptLoaded();”;
function $declare(functionName) {
eval(String.format(template, functionName));
$(‘body’).attr(‘xmlns:’ + functionName.toLowerCase(), ‘javascript:jQueryDeclarative.’ + functionName);
}
$.extend({ declare: $declare });
})(jQuery);

2. Notice that eventhough the template is assigned to the class=”sys-template” , it is not bound yet. We will do that in next step.
Reusing widget client template
<div id=”col1″ class=”col” sys:attach=”dataview” dataview:data=”{{ widgets }}” dataview:itemtemplate=”widget-template”></div>
<div id=”col2″ class=”col” sys:attach=”dataview” dataview:data=”{{ widgets }}” dataview:itemtemplate=”widget-template”></div>
<div id=”col3″ class=”col” sys:attach=”dataview” dataview:data=”{{ widgets }}” dataview:itemtemplate=”widget-template”></div>
These above div tags are actually binding JSON array to widget client template.
One important thing to note in above code is the line <div code:if=”col==$element.id” id=”{{‘widget-‘ + id}}” class=”widget”>. This ensures that only the associated widgets are placed in correct columns. In other words, all widgets associated to Col1 are placed in Col1 DIV tag, Col2 widgtes are placed in Col2 DIV tag and hence forth.
In my next blog post, I will walk you through on how to provide drag and drop functionality to widgets and prepare interfaces for storing widget location and status in the server.


/*
* jQuery Declarative plug-in 0.1 (3/12/2008)
*
* Copyright (c) 2008
* Brian J. Cardiff –
http://weblogs.manas.com.ar/bcardiff/
*
* Built upon
* jQuery 1.2.6 (
http://jquery.com)
* Microsoft Ajax (MicrosoftAjax.js)
* Microsoft Ajax Templates (MicrosoftAjaxTemplates.js)
*
*/
(function($) {
Type.registerNamespace(“jQueryDeclarative”);
var template = “jQueryDeclarative.{0} = function(element) {{ jQueryDeclarative.{0}.initializeBase(this, [element]); }}; jQueryDeclarative.{0}.prototype = {{ initialize: function() {{ jQueryDeclarative.{0}.callBaseMethod(this, ‘initialize’); jQuery(this.get_element()).{0}(this); }}, dispose: function() {{ jQueryDeclarative.{0}.callBaseMethod(this, ‘dispose’); }} }}; jQueryDeclarative.{0}.registerClass(‘jQueryDeclarative.{0}’, Sys.UI.Behavior); if (typeof (Sys) !== ‘undefined’) Sys.Application.notifyScriptLoaded();”;
function $declare(functionName) {
eval(String.format(template, functionName));
$(‘body’).attr(‘xmlns:’ + functionName.toLowerCase(), ‘javascript:jQueryDeclarative.’ + functionName);
}
$.extend({ declare: $declare });
})(jQuery);