Lakkakula's Blog

June 18, 2009

Developing Web 2.0 Portal using Asp.Net MVC, Microsoft Ajax Client Templates and jQuery with drag and drop widget personalization – Part 1

Filed under: Ajax Client Templates, Asp.Net, Asp.Net MVC, jQuery, MVC — Venkata Uma lakkakula @ 2:35 pm

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.

linksgrid_db

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

wiz_list.png.

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:

  • Client Code
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" ... }
  • Server Code
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:

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);

widget

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.

Shout it

kick it on DotNetKicks.com

/*
* 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);

Advertisement

27 Comments »

  1. […] Originally posted here: Developing Web 2.0 Portal using Asp.Net MVC, Microsoft Ajax Client Templates and jQuery with drag an… […]

    Pingback by Developing Web 2.0 Portal using Asp.Net MVC, Microsoft Ajax Client Templates and jQuery with drag and drop widget personalization – Part 1 | Web 2.0 Designer — June 18, 2009 @ 6:32 pm

  2. Developing Web 2.0 Portal using Asp.Net MVC, Microsoft Ajax Client Templates and jQuery with drag and drop widget personalization – Part 1 « Lakkakula’s Blog…

    Thank you for submitting this cool story – Trackback from DotNetShoutout…

    Trackback by DotNetShoutout — June 19, 2009 @ 5:38 am

  3. […] 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. […]

    Pingback by Web 2.0 Portal using Asp.Net MVC, Microsoft Ajax Client Templates and jQuery with drag and drop widget personalization « Lakkakula's Blog — June 21, 2009 @ 1:29 pm

  4. […] to VoteDeveloping Web 2.0 Portal using Asp.Net MVC, Microsoft Ajax Client Templates and jQuery with drag an…Thursday, June 18, 2009 from lakkakula.wordpress.comNote: Check out the working portal at […]

    Pingback by ASP.NET MVC Archived Blog Posts, Page 1 — June 22, 2009 @ 12:47 am

  5. Great post. I like you how laid out the database setup first. I posted on a very similar project on the same day, except I started from the jQuery/CSS portion and plan on posting on the database/MVC code parts later.

    http://www.philderksen.com/2009/06/18/drag-and-drop-categorized-item-list-with-jquery-and-aspnet-mvc-part-1/

    Comment by Phil Derksen — June 22, 2009 @ 2:04 pm

  6. Very cool, waiting every morning for the next parts to this series

    Thanks

    Comment by Prashant — June 25, 2009 @ 11:05 am

  7. Hi Uma,

    Thanks for your post. It was really helpful. When i was trying to implement this, i am having issues with

    Can you please share the source code uptil now?

    Thanks,
    Chai

    Comment by chaitanya — July 1, 2009 @ 2:05 pm

  8. Hi Venkata,
    FYI: The site linksgrid.com seems to be down at the moment.

    – prashant

    Comment by Prashant — July 2, 2009 @ 10:42 am

  9. I was looking to do this exact thing on a current project of mine. Anxiously waiting for you to finish up the series, looks great so far!

    Comment by JM — July 31, 2009 @ 12:55 pm

    • @JM, Appreciate your interest. It’s folks like you keep my enthusiasm up:) I will try to finish this series ASAP

      Comment by Venkata Uma lakkakula — July 31, 2009 @ 5:52 pm

  10. Hi
    I tried following the method shown and declared

    I keep getting unrecognized namespace ‘ajaxload’ message.

    Do I need any dependency js files?

    Thanks
    Prashant

    Comment by Prashant — July 31, 2009 @ 3:54 pm

    • @Prashant, you need following method in your page:

      (function($) {
         $.fn.extend({
            ajaxload: function(options) {
               return this.load(options.url);
            }
         });
      })(jQuery);

      Then you can declare load method like this:

      $(function() {
         $.declare("ajaxload");
      });

      Comment by Venkata Uma lakkakula — July 31, 2009 @ 5:40 pm

      • Hi Venkata
        I created a file called “ajaxload.js” and put the javascript you have shown in it. This file is reference as script in by default.aspx

        [div] sys:attach=”ajaxload” ajaxload:url=”{{RSSReader.asmx/GetRSSReader}}” [/div]
        I put the square bracket above since the angle brackets get stripped off in the comments

        Still no luck same error….am I missing something

        Thanks
        – Prashant

        Comment by Prashant — August 1, 2009 @ 10:09 am

      • Prashant,

        As I mentioned in my blog post, you need jQuery 1.2.6 or higher version and also need declarative.js written by Brian J. Cardiff. Refer the declarative jQuerycode in post.

        Finally, you may want to change your url to: [div] sys:attach=”ajaxload” ajaxload:url=”RSSReader.asmx/GetRSSReader” [/div]

        If the code still doesn’t work, send me some sample app so that I can take a look and fix it.

        Comment by Venkata Uma lakkakula — August 1, 2009 @ 10:51 am

  11. I got it working now. Thanks for all your help.

    Comment by Prashant — August 1, 2009 @ 1:17 pm

  12. I’m sorry for being ignorant on this but I’m trying to install Linksgrid and the readme says to install visual studio 2008 express edition, sql 2008 express edition and MS MVC1.0 which I have done.

    I’m running into a problem when I click on liksgridv1.0.sln. MS visual web devl 2008 express edition opens and I get this msg, “the project file linksgridv1.0.csproj cannot be opened. The project type is not support by this installation.

    I have asp.net 3.5 installed, MS MVC, MSSQL 2008 express. Am I doing something wrong.

    Comment by Matt — August 12, 2009 @ 6:45 pm

  13. @Matt,
    1. try the instructions given in this blog: http://weblogs.asp.net/leftslipper/archive/2009/01/20/opening-an-asp-net-mvc-project-without-having-asp-net-mvc-installed-the-project-type-is-not-supported-by-this-installation.aspx
    2. Optionally you can open visual web developer 2008 and select File->Open Web Site… and then select File System from left pane and then select the folder where you have unzipped the linksgrid project.

    If none of the above work, un-install Asp.net MVC 1.0 (and any previous beta versions of MVC) from your PC, re-boot your PC and then install MVC 1.0 again.

    Please let me know if you still have the problems, I would be a happy to assist you further.

    Comment by Venkata Uma lakkakula — August 12, 2009 @ 7:13 pm

  14. Nice Post
    Informative and useful one
    I am .Net Developer and I am looking for this
    Thanks for the great stuff.

    Comment by Nimesh – Perception System — August 29, 2009 @ 5:46 am

  15. I’m so glad I found this site…Keep up the good work I read a lot of blogs on a daily basis and for the most part, people lack substance but, I just wanted to make a quick comment to say GREAT blog. Thanks,

    A definite great read…

    -Bill-Bartmann

    Comment by Bill Bartmann- — September 14, 2009 @ 4:30 am

    • The code got stripped…

      onclick=”{{‘return $.ajaxUtil.navigate(this, \’widget-content\’,\’/’ + controller + ‘/’ + action + ‘/’ + id + ‘\’);’}}”>

      Comment by Pat — December 31, 2009 @ 5:47 pm

      • anaxUtil.navigate is a custom method which will relpace all the contents of the widget and not just footer, so you might want to create another method similar to navigate which will update only the footer. please let me know if you still have problems.

        Comment by Venkata Uma lakkakula — January 1, 2010 @ 2:11 am

      • Hi Venkata,

        I was able to get it to work by updating that navigate method. Thanks for pointing me in the right direction!

        var target;
        var targetfooter;
        var url;
        if (action) {
        target = $(anchor).parents(‘.widget:first’).children(‘.widget-body:first’).children(‘.widget-content:first’);
        targetfooter = $(anchor).parents(‘.widget:first’).children(‘.widget-footer:first’);
        url = action;
        }

        $(targetfooter).html($.ajaxUtil.DisplayDateTime());
        DisplayDateTime: function(){
        var currentDate = new Date();
        var day = currentDate.getDate();
        var month = currentDate.getMonth() + 1;
        var year = currentDate.getFullYear();

        var hours = currentDate.getHours();
        var minutes = currentDate.getMinutes();
        var seconds = currentDate.getSeconds();
        var suffix = ” AM”;
        if (hours >= 12) {
        suffix = ” PM”;
        hours = hours – 12;
        }
        if (hours == 0) {
        hours = 12;
        }
        if (minutes < 10) {
        minutes = "0" + minutes;
        }
        if (seconds < 10) {
        seconds = "0" + seconds;
        }
        return month + "/" + day + "/" + year + " " + hours + ":" + minutes + ":" + seconds + suffix;

        Comment by Pat — January 8, 2010 @ 3:57 pm

  16. Hi Venkata,

    Great work with LinksGrid! I’m glad I found before I tried to reinvent the wheel. I was wondering if you could help me with something. I’m looking at the refresh control for a widget and would like to replace the html of widget-footer with an updated datetime stamp. Here’s the line of code I’m trying to modify.

    I tried updating adding another line of javascript but it didn’t seem to like that. Any suggestions?

    Thanks,
    Pat

    Comment by Pat — December 31, 2009 @ 5:44 pm

  17. Hi Venkata,

    I’m having another issue I’m hoping you could help me work through. Everything works fine if the user goes to default.aspx but if a user goes to Home/Index the page loads but the json call to WidgetList is never called and the widgets don’t appear. I’m trying to create different sets of widgets for a user assigned to pages that can be directly accessed from a browser link. I have a route setup to direct these url’s to the Home controller’s Index action so it can set the page and load the widgets. The client template doesn’t seem to be populated even though the javascript to fill the Sys.Obserser with a set of widgets seems to be firing when the page loads.

    Comment by Pat — December 31, 2009 @ 8:56 pm

    • I would be glad to assist you, in my opinion default.aspx and home/index should not be treated as different as we are working with MVC. You might want to check all the urls (dynamic/static) referenced in the code are pointing to correct path representing action method and controller.

      Comment by Venkata Uma lakkakula — January 1, 2010 @ 2:09 am


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Blog at WordPress.com.

%d bloggers like this: