skip to main | skip to sidebar

Popular-on-Twitter Widget: Topsy-enabled jQuery Plugin
Another Topsy-enabled jQuery plugin to list the most popular posts on your website, from your Twitter timeline or for some keyword within a selected period of time.
Links are displayed with number of tweets and score -which is calculated by the influence of the people talking about them and affects on sorting those links.


AdvertiseSpace is a new advertising marketplace was just launched a couple days ago, It will let you sell ads directly on your website at monthly fixed prices. Which could get you 200% to 500% increase in your site revenue when compared to cost per click programs as Google AdSense!

12 Google Wave Invites Giveaway
Still haven't a peek at Google Wave yet? here is your chance.. I have 12 invites to give without any freaky conditions. All you need to do to get invited is to email me on Mike[at]moretechtips.net from the email that you would like to receive the invite at.

Who-Tweet Button - Fancy jQuery Plugin for Twitter
I took the Topsy retweet button and jQuery-fied it, then added the "Who" part to it using the awesome Topsy API. to create this fancy share count button for your stories on twitter. that's it!

Twitter Trackbacks Widget - A jQuery Plugin
Highly customizable Twitter trackbacks widget to integrate tweets that mention your post into your blog. Each tweet comes with reply & retweet links to get more readers engaged in your story conversation.

Getting Closer to Google Closure

After Google Introduced Closure Library I wanted to get a closer look on it. here's what I've come to.



Topsy has just released a Wordpress plugin that gives you another retweet button. But, what if you can only work with a JavaScript widget? here is a small hack..

I needed to use the tall Delicious save button, but I found that it wasn't tall at all.. it was short & fat :)
So, I had to shrink it a little by hiding some parts. And, since Delicious was smart enough to implement their button using AJAX. It is easy to customize the button using CSS.

Twitter Friends & Followers Widget - A jQuery Plugin

There is a Facebook fans widget, Google friends widget, what about a Twitter friends widget?!
Here is a jQuery plugin that you can embed anywhere to display pictures of your Twitter followers or friends (whom you follow) and their latest tweets if you like.

By featuring your Twitter friends or followers on your blog, you will encourage others to become friends too..

Realtime Related Stream Bar; Collecta-powered jQuery plugin
This is a jQuery plugin to create a bar of real-time stream of information related to your post powered by Collecta search engine. Collecta monitors the streams of news sites, popular blogs and social media. So it can show you results as they happen. There are five content categories in Collecta - updates, stories, comments, photos, and videos.

Check these Demos..

Realtime Related Tweets Bar: Another jQuery Plugin

With all the buzz lately about Twitter real-time search. Why don't you add a real-time tweets bar related to your posts from your twitter timeline or from anybody or even limit it by a geocode coordinates!

Check out These Demos..
Each one links to the demo page where you can see HTML & CSS & JS you need to use... CSS code is important but it is almost the same across those different samples, so I'm not going to focus on it here.

Blogger said: Show your face!
Google Blogger just Added another feature to show commenter avatar! So again. this is great, but if you have a customized template the avatars won't show up. So how to fix that..

Blogger said 'You Might As Well Jump'! Great, but..

Google Blogger has added new feature, an easy way to show posts summaries on your blog index page! But if you still don't see the "read more" link like I did! Google knows why, But I had to dig into it a little bit more..

@Google: you got Pages, Sites and Blogspot... why?!
After Google Blogger 10th anniversary.. Google Blogger promises that many of our dreams as Bloggers will come true so soon.. by dreams I mean "Product Ideas for Blogger" which Google has launched on last April to collect users votes & feature requests on blogger.

Paging ListboxFew months ago I posted a Javascript class on how to Implement a paging listbox using jQuery. which has drawn a lot of traffic and few questions lately so I thought it would be more convenient to rewrite the code as jQuery plugin and make few enhancements plus providing a complete sample code in VB.Net and C#.

Enhancements:

  1. Easier usage, You only need to insert a div with the class "paging-listbox" and settings inside "options" attribute to automatically have the paging listbox loaded inside that div. still you can load the listbox with regular Javascript call.
  2. Added support for right-to-left layout.
  3. You can pass additional parameters to source page via AJAX. for example a category ID that user selects from another form field and should be used to query records by on the source page.
  4. Few visual enhancements.

Custom Field Images PluginHave you spent much time adding posts thumbnails using custom fields.. or -like me- spent much time trying many plugins that either not working well or very limited.

I was always not convinced with "Read more" built-in option in WordPress. As it is very unlikely to use the starting portion of your post as a teaser or summary.

Google Blog Bar; jQuery-ed Version My first jQuery Plugin.. A 4KB jQuery plugin instead of a 118KB Google Blog Bar!!

I have shown before how to use Google Blog Bar as related posts widget, But when I actually tried to use it on my site, I didn't like all these JavaScript/CSS files that I've to include.
And since I already use jQuery -and who doesn't- I decided to rebuild the Blog Bar from scratch.

Google AJAX Search APIThe Google AJAX Search API provides simple web objects that perform inline searches over a number of Google services (Web Search, Local Search, Video Search, Blog Search, News Search, Book Search, Image Search, and Patent Search).

It is an old news that: The API exposes a raw RESTful interface that returns JSON encoded results so it would be easily processed by most languages and runtimes.

Related Posts Widget Powered by GooglePreviously, I proposed a related posts widget for Blogger based on Blogger Data API .
This time will do simple adjustments on 2 Google AJAX Search API controls to work as related posts widgets. which should work on any blog as opposed to the previous method which was limited to Blogspot blogs only.

What is the Google AJAX Search API?

The Google AJAX Search API lets you put Google Search in your web pages with JavaScript. You can embed a simple, dynamic search box and display search results in your own web pages or use the results in innovative, programmatic ways :)

Blogger Data APIOn this post I gonna walk you through the code behind my new widget [A Smarter Related Posts widget for Blogger].

If you just want to install the widget on your blog, go to A Smarter Related Posts widget for Blogger.

To see the widget in action, Scroll down to the end of this post. also hover on links to see relevancy score.

Blogger Data APIDisplaying a related posts is a smart way for keeping your site visitors around. and you may have seen other Related Posts Widgets out there.. but this one will be Easier and Smarter.

* Check Out New Edition
of this widget that is loaded with pretty new features like posts thumbnails and transition effects..

Check features list:
  1. Easier : since All of the work is(has to be) done on the client side, You will not need to modify your template at all. widget will read post tags that blogger already display with each post.
  2. Smarter : Cause This widget will list the top related 5(or more) posts sorted by relevancy!Top relevant posts are the posts that have more tags in common with the current post.
  3. More Optimized: Will only use summary feeds instead of querying default feeds that return full contents!
  4. This widget also can work as a recent posts widget when there is no tags to aggregate.
  5. Can be also used to get related posts from another blogspot blog. And in that case you don't have to run it on a blogspot!
  6. A fixed list of tags can be used to aggregate.
  7. It can display loading text or icon until widget is loaded.
  8. Related posts list can be styled based on relevancy!
  9. You can specify either to load widget on document ready or on window load.
  10. Widget will be attached to container specified or it will be placed where you made the JavaScript call.
To see the widget in action, Scroll down to the end of this post. also hover on links to see relevancy tip.

This is a second post about Social bookmarking widgets.. Where I provide a better way to integrate them in your pages and also asking for your feedback on my proposed standardization specifications :)

Scroll down to the end of this post to see widgets in action..

After I posted the previous [Creating Uniform Social Bookmarking Widgets], I faced some issues that are worth mentioning and polling too.

We all like social networks but no-network is perfect! each network gives us a different JavaScript code with different parameters to install a different looking widget on each article.. Now I need and will do something about it.

To do something uniform: I'll have to pick one common style across those widgets.. The most popular style around is the 'Tall' one and it also looks like web 2.0.

Tag-Cloudfor those who want or can only use JavaScript to convert regular tags list into a Tag-Cloud. jQuery and regular expressions are here to help.
Why Tag-Cloud? It is better looking and more meaningful.

Update:
Since Aug. 25, 2009: blogger now offers a built-in Tag-cloud. So you won't need this code for that job. still, jQuery manipulation is cool :)

Template by eblogtemplates.comI had a recent experience with changing my blogger template.. Now it is time to share some tricks to simplify things with next one trying to change it.. cause you should do it especially if you are still working with those 800x600 built-in blogger templates.

SEOMeta keywords are very important to do better SEO for your blog.. And you should know that Search engines look for different description and keywords for each page of your site. If you put the same thing across all pages of your site, search engines may regard this as spam.

I saw lots of examples on how to add meta title/description based on the post title for Blogger/Blogspot. but I couldn't find one for using labels/Tags as meta keywords for each post.

This is a complete javascript example to detect your site visitors referrer and greeting them with a message based on their referrer url.
For example, when a dzone user clicks on your link from dzone site, they get a message to remind them to vote up your site.

If you didn't see the message already, click here to see a greeting box..
Or Try pressing 'enter' on the Browser address bar to see the no-referrer message.
Or click here to google this post title then click on my site link to get referred back here and see the googler message.

The code consists of few CSS to style the message box and uses jQuery to show up the message box and array of regular expressions to define each referrer URL pattern and the corresponded message.
<html>
<head>
<title>jQuery Fun: Greeting Your Site Referrals</title>
<style type="text/css">
   /* Style your Message Div */
   #WelcomePlaceHolder{
      /* Keep hidden until called by javascript */
      display:none;
      Border:silver 2px solid;
      width:240px;
      height:125px;
      /* some space for BG image */
      padding:2px 2px 2px 100px;
      background:url('http://www.01gif.com/base/les_gifs_personnage_humains/hommes/hommes002.gif') no-repeat left center;
      font-size:25px;
      color:#333333;
      margin:5px;
   }
   /* Style Close Button */
   A.CloseButton {
      font-size:11px;
      font-weight:bolder;
      color:black;
      border:silver 2px solid;
      text-decoration:none;
      float:right;
      padding:2px;
   }
   A.CloseButton:hover {
      border:gray 2px outset;
      text-decoration:none;
   }
</style>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript">
//Add urls regular expressions and your message(any HTML Code) to the array
var msgs = [
//null url : to show a message for direct traffic (no referer, some one who remember your site url!)
{'url':null,                           'msg':'I am glad you remember my site URL, enjoy your stay'}
//My url! : show message for referrals from your own site or null so you do not annoy them
,{'url':/^http:\/\/(\w+\.)?moretechtips\.net/,    'msg':null}
//Other urls
,{'url':/^http:\/\/(\w+\.)?google\.com/,      'msg':'Welcome googler, Hope you will find what you looking for'}
,{'url':/^http:\/\/(\w+\.)?dzone\.com/,         'msg':'Welcome fellow dzone reader, if you like it please vote it up'}
,{'url':/^http:\/\/(\w+\.)?digg\.com/,         'msg':'Welcome digger, if you like it please digg it'}
,{'url':/^http:\/\/(\w+\.)?propeller\.com/,      'msg':'Welcome propeller user, hope you will like it here'}
//generic pattern: to show generic message for referrers that you did not specify one for
//You must keep it at the end of the list as it will match any non empty referrer
,{'url':/^http:\/\//,               'msg':'Hello their.. Hope you will find what you looking for'}
];
function DetectReferrer(){
   var div = $('#WelcomePlaceHolder');
   //if Div was not placed means , not to show message
   if (!div.length) return;
   var ref = document.referrer.toLowerCase();
   var msg = findMatch(ref);
   // if not null msg found
   if(msg) {
      //Add Close Button and Show up message
      div.html( '<a href="javascript:void(0)" class="CloseButton">X</a>' + msg).show('slow',function(){
         //Hide On click close Button
         $('.CloseButton',div).click(function(){ div.hide() })
      });
   }
}
function findMatch(ref) {
   for(var i=0; i<msgs.length; i++)
      if( ( ref=='' && msgs[i].url==null) || (ref>'' && ref.match(msgs[i].url) ) )
         return msgs[i].msg;
   return null;
}

// Call the Referrer Detector function on document ready
$(DetectReferrer);
</script>
</head>
<body>
<div id="WelcomePlaceHolder"></div>
</body>
</html>


Note that patterns array includes 4 type of patterns (in lowercase):
  1. Null Pattern, to add a message for direct traffic visitors (no-referrer)
  2. Your Site Pattern, to match when people referred by your site links. and i kept the message 'null' so i don't annoy the visitors.
  3. Generic pattern to match any referrer you didn't specify a message for, and it must come at the end of the array as it will match any non-empty referrer url.
  4. The rest are Sites Patterns.
    for example [/^http:\/\/(\w+\.)?google\.com/] will match referrer urls that:
    [^] Start with
    [http:\/\/] means 'http://'
    [(\w+\.)?] Any -optional- sub domain
    [google\.com] match 'google.com'

Also note that, you should put the place holder div (ID='WelcomePlaceHolder') where the message should appear, and if you didn't put it there no message will come up.

If you need to review regular expressions, check this good reference [RegExp Object Reference]

Many times in web forms we need to provide a listbox with lots of elements, A good solution is to filter and page these elements to speedup page loading and optimize DB/Network usage..

Update! I've rewritten the code as jQuery plugin, made few enhancements and provided a complete sample code in VB.Net and C#. check-out the new plugin of Paging Listbox.

This is a complete implementation of a paging listbox using jQuery on client side and ASP.Net on server side. and should look like this..
Paging Listbox
The form web page, which also contains the JavaScript class to build the select box, of course don't forget to download the jQuery library.
<html>
<head>
<title>jQuery & Ajax Fun: Implementing a Paging Listbox</title>
<script type="text/javascript" src="JS/jquery-1.3.min.js"></script>
<script type="text/javascript">
function AjaxListBox() {
   this.source = '';
   this.divID= '';
   this.keyID= '';
   this.buttonID= '';
   this.lastKey= '';
   this.startup= true;
   this.minWidth=0;
   this.position = {'top':0,'left':0};
   var self = this;
   this.init= function() {
      $(document).ready(function(){
         //calc position and min-width for listbox
         self.minWidth = $('#'+self.keyID).width()+ $('#'+self.buttonID).width()+4;
         self.position = $('#'+self.keyID).position();
         self.position.top= self.position.top + $('#'+self.keyID).height()+2;
         // Position and hide div
         $('#'+self.divID).css({'display':'none','border':'gray 1px solid','position':'absolute','z-index':5,'top':self.position.top,'left':self.position.left});
         // bind onclick handler for 'toggle' button
         $('#'+self.buttonID).bind('click',null,self.toggle);
         // bind onkeydown handler for 'Key' textinput and call find function
         $('#'+self.keyID).bind('keydown',null,self.keydown);
         //load list
         self.load();
      });
   }
   this.load= function(key,pi) {
      if(key==null ||key=='undefined') key='';
      if(pi==null ||pi=='undefined') pi='';
      //Save key to use when move through pages
      this.lastKey= key;
      
      $('#'+this.divID).html('please wait..');
      $.get(this.source,{'key':key,'pi': pi},this.loaded,'html' );
   }
   this.loaded = function(data,txtStatus) {
      //Set Inner html with response of Ajax request
      $('#'+self.divID).html(data);
      $('#'+self.divID+' > select').css({'border-width':'0'});
      //Add handler for onchange to reload when another page is requested
      $('#'+self.divID+' > select').bind('change',null,self.change);
      //Add handler for onblur to hide box
      $('#'+self.divID+' > select').bind('blur',null,self.hide);

      if (self.startup) self.startup=false;
      else self.show();
   }
   this.change = function() {
      //Get Value of Select Box
      var v = $('#'+self.divID+' > select').val();
      //To do paging the value must be like 'pi=2' which means go to page 2
      if (/^pi=\d+$/i.test(v)) {
         var pi= v.replace(/pi=/i,'');
         self.load(self.lastKey,pi);
      }   
   }
   this.toggle = function(e) {
      if ($('#'+self.divID).css('display')=='none') self.show();
      else self.hide();
   }
   this.show = function(e){
      $('#'+self.divID).show();
      //Insure width is more than min-width
      var w = $('#'+self.divID+' > select').width();
      if (w>0 && w<self.minWidth) $('#'+self.divID+' > select').width(self.minWidth);
   }
   this.hide = function(e){
      $('#'+self.divID).hide();
   }
   this.find = function() {
      //text to search for
      self.load($('#'+self.keyID).val());
   }
   this.keydown = function(e) {
      // this will catch pressing enter and call find function
      var intKey = e.keyCode;
      if(intKey == 13) {
         self.find();
         //and prevent submitting the form that contain this input box
         return false;
      }   
   }
}
</script>
<style type="text/css">
   * {
      font:12px arial
   }
   .AjaxListBoxKey {
      border:gray 1px solid;
      width:120px;
   }
   .AjaxListBoxKeyBTN{
      border:silver 1px solid;
      background-color:#333333;
      color:white;
      padding:.5px;
      font:12px arial;
   }
</style>
</head>
<body>
   <form id="form1" action="" method="post">
      Select Product
      <input id="key" name="key" type="text" class="AjaxListBoxKey" /><input type="button" id="find" class="AjaxListBoxKeyBTN" value="&#9660;" />
      <div id="box"></div>
      <script type="text/javascript">
         var box = new AjaxListBox();
         box.source = "listbox.aspx";
         box.divID = "box";
         box.keyID = "key";
         box.buttonID= "find";
         box.init();
      </script>
    </form>
</body>
</html>


The server side page(whatever the language is!) is requested to handle query string parameters (pi: PageIndex, key: Search keyword) and response with just a list box(no other tags!) that contains the matched elements at the requested page index, plus 2 extra elements to go to previous and next pages indexes with value like 'pi=3' which means go to page 3. Of course you can select the PageSize and listbox size that works for you.

Here is server side page "listbox.aspx"
<%@ Page Language="VB" %>
<script runat="server" language="VB">
   Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
      Dim key As String = Request.QueryString("key") & ""
      Dim PageIndex As Integer = 1
      Try
         PageIndex = Integer.Parse(Request.QueryString("PI"))
      Catch ex As Exception
         PageIndex = 1
      End Try

      Dim ps As New Products
      ps.PageSize = 5
      ps.PageIndex = PageIndex
      ps.SelectItems(Product.ProductStatus.Active, key)
      Response.Write(ps.PagingBox("ProductID", 0, , , 5))
      ps = Nothing
   End Sub
</script>


On Listbox.aspx I'm using my favorite[Traditional yet Powerful : Data Access Layer for ASP.Net] to access the products Table after adding the following function to the collection class to build the listbox.
Public Function PagingBox(ByVal FieldID As String, ByVal SelectedID As Integer, Optional ByVal FieldClass As String = "", Optional ByVal Onchange As String = "", Optional ByVal size As Integer = 0) As String
   Dim ret As New StringBuilder("<select name=""" & FieldID & """ id=""" & FieldID & """")
   If FieldClass > "" Then ret.Append(" FieldClass=""" & FieldClass & """")
   If Onchange > "" Then ret.Append(" Onchange=""" & Onchange & """")
   If size > 0 Then ret.Append(" size=""" & size + 2 & """")
   ret.Append(">")

   If Me.Count = 0 Then
      ret.Append("<option value="""">-- No Results! --</option>")
   End If

   If PageIndex > 1 Then
      ret.Append("<option value=""pi=" & (PageIndex - 1) & """>" & _
             "-- to Page " & (PageIndex - 1) & " of " & PageCount & " --</option>")
   End If
   For i As Integer = 0 To Me.Count - 1
      Dim P As Product = Item(i)
      ret.Append("<option value=""" & P.ID & """")
      If P.ID = SelectedID Then ret.Append(" selected")
      ret.Append(">" & P.Name & "</option>")
   Next

   If PageIndex < PageCount And PageIndex > 0 Then
      ret.Append("<option value=""pi=" & (PageIndex + 1) & """>" & _
             "-- to Page " & (PageIndex + 1) & " of " & PageCount & " --</option>")
   End If

   ret.Append("</select>")
   Return ret.ToString
End Function


Many greetings to jQuery folks..

After i posted [Traditional yet Powerful : Data Access Layer for ASP.Net].. it is time to extend it to support a multi-language ASP.Net Application..

To support many languages.. Products table should be split into 2 tables. one for basic information and the other for language-dependent fields. here is the SQL Create script:
CREATE TABLE [dbo].[Products](
   [ID] [int] PRIMARY KEY IDENTITY(1,1) NOT NULL,
   [Status] [tinyint] NOT NULL DEFAULT (0),
   [Price] [decimal](18,0) NOT NULL DEFAULT (0),
)

CREATE TABLE [dbo].[ProductsDetails](
   [ID] [int] NOT NULL DEFAULT (0),
   [Lang] [varchar](2) NOT NULL DEFAULT (''),
   [Name] [nvarchar](50) NOT NULL DEFAULT (''),
   [Description] [nvarchar](500) NOT NULL DEFAULT (''),
   CONSTRAINT [PK_ProductsDetails] PRIMARY KEY CLUSTERED
   (
      [ID] ASC,
      [Lang] ASC
   )
)

You should notice that multi-language text fields are now saved in nvarchar instead of varchar to support Unicode.

Now, DAL classes need few changes and adding another 2 classes
The Entity Class for Products
Imports System.Data
Imports System.Data.Common
Imports Microsoft.VisualBasic

Public Class Product
   Public Enum ProductStatus
      None = 0
      Active = 1
      Inactive = 2
   End Enum

   Public ID As Integer
   Public Price As Decimal
   Public Status As ProductStatus
   ''' <summary>Detail Collection of this product</summary>
   Public Details As ProductsDetails
   ''' <summary> if GetLang=Nothing (default) no Detail Records is queried
   ''' if GetLang="EN" then Detail(EN) is queried and added to the details Collection and you can refer to it by .Detail property
   ''' if GetLang="" then All Details records are queried and add to details collection and you can refer to each of them by .Detail(Lang) property
   ''' </summary>
   Public GetLang As String = Nothing

   Public Sub New(Optional ByVal ProductID As Integer = 0)
      SelectItem(ProductID)
   End Sub

   Public Sub New(ByVal ProductLang As String, Optional ByVal ProductID As Integer = 0)
      GetLang = ProductLang
      SelectItem(ProductID)
   End Sub

   ''' <summary>Initialize Product Fields</summary>
   Public Sub Initialize()
      ID = 0
      Price = 0
      Status = ProductStatus.Active
      If Details Is Nothing Then
         Details = New ProductsDetails
      Else
         Details.Clear()
      End If
   End Sub

   ''' <summary>Return a Detail Object based on Lang param. If Lang was not specified return first Detail Object found</summary>
   Public ReadOnly Property Detail(Optional ByVal Lang As String = "") As ProductDetail
      Get
         If Details.Count = 0 Then Return Nothing
         If Lang = "" Then Return Details(0)
         For i As Integer = 0 To Details.Count - 1
            If Details(i).Lang.ToUpper = Lang.ToUpper Then
               Return Details(i)
            End If
         Next
         Return Nothing
      End Get
   End Property

   Friend Sub Populate(ByRef dr As DbDataReader)
      Populate(CType(dr, Object))
   End Sub
   Friend Sub Populate(ByRef dr As DataRow)
      Populate(CType(dr, Object))
   End Sub
   Private Sub Populate(ByRef dr As Object)
      ID = CInt(dr("ID"))
      Price = CDec(dr("Price"))
      Status = CByte(dr("Status"))
   End Sub

   ''' <summary>Select Product by ID</summary>
   Public Sub SelectItem(ByVal ProductID As Integer)
      Call Initialize()

      If ProductID = 0 Then Exit Sub

      Dim cmd As New GenericCommand("SQLConn")
      Dim rdr As DbDataReader
      'Select Query
      cmd.CommandText = "Select Top 1 Products.* "
      If GetLang > "" Then cmd.CommandText += ",Lang,Name,Description"
      'from
      cmd.CommandText += " from Products"
      If GetLang > "" Then cmd.CommandText += ",ProductsDetails"
      'where
      cmd.CommandText += " Where Products.ID=@ID"
      If GetLang > "" Then cmd.CommandText += " and ProductsDetails.ID=Products.ID and Lang=@Lang"
      cmd.AddParam("@ID", ProductID)
      If GetLang > "" Then cmd.AddParam("@Lang", GetLang)

      rdr = cmd.ExecuteReader()
      If rdr.Read() Then
         Populate(rdr)
         If GetLang > "" Then
            '' Add Detail
            Dim det As New ProductDetail
            det.Populate(rdr)
            Details.Add(det)
            det = Nothing
         End If
      End If
      rdr.Close()
      rdr = Nothing
      cmd = Nothing

      If GetLang = "" And GetLang IsNot Nothing And ID > 0 Then
         Details.SelectItems(ID)
      End If
   End Sub

   ''' <summary>Insert new Product and get new ID</summary>
   Public Sub InsertItem()
      If ID <> 0 Then Exit Sub

      Dim cmd As New GenericCommand("SQLConn")
      cmd.CommandText = "Insert Into Products (Price,Status) Values (@Price,@Status)"
      cmd.AddParam("@Price", Price)
      cmd.AddParam("@Status", Status)
      ID = CInt(cmd.ExecuteIdentity())
      cmd = Nothing

      'Insert Details if any
      For i As Integer = 0 To Details.Count - 1
         'first: set new ID on details objects
         Details(i).ID = ID
         Details(i).UpdateOrInsertItem()
      Next
   End Sub

   ''' <summary>Update Product</summary>
   Public Sub UpdateItem()
      If ID = 0 Then Exit Sub

      Dim cmd As New GenericCommand("SQLConn")
      cmd.CommandText = "Update Products set Price=@Price,Status=@Status where ID=@ID"
      cmd.AddParam("@Price", Price)
      cmd.AddParam("@Status", Status)
      cmd.AddParam("@ID", ID)
      cmd.ExecuteNonQuery()
      cmd = Nothing

      'Update Details if any
      For i As Integer = 0 To Details.Count - 1
         Details(i).UpdateOrInsertItem()
      Next
   End Sub

   ''' <summary>Delete This product</summary>
   Sub DeleteItem()
      If ID = 0 Then Exit Sub

      Dim cmd As New GenericCommand("SQLConn")
      cmd.CommandText = "DELETE FROM Products WHERE ID=@ID"
      'will assume that you set relationship between the 2 tables with 'cascade delete' to delete Details records when product is deleted
      ' or delete them by adding another query "DELETE FROM ProductsDetails WHERE ID=@ID"
      cmd.AddParam("@ID", ID)
      cmd.ExecuteNonQuery()
      cmd = Nothing

      Call Initialize()
   End Sub

   Protected Overrides Sub Finalize()
      Details = Nothing
      MyBase.Finalize()
   End Sub
End Class


The Entity Class for Products Details
Public Class ProductDetail
   Public ID As Integer
   Public Lang As String
   Public Name As String
   Public Description As String

   Public Sub New()
      Initialize()
   End Sub

   Public Sub New(ByVal ProductID As Integer, ByVal ProductLang As String)
      SelectItem(ProductID, ProductLang)
   End Sub

   ''' <summary>Initialize Fields</summary>
   Public Sub Initialize()
      ID = 0
      Lang = ""
      Name = ""
      Description = ""
   End Sub

   ''' <summary>Select Details by ID and Lang</summary>
   Public Sub SelectItem(ByVal ProductID As Integer, ByVal ProductLang As String)
      Call Initialize()

      If ProductID = 0 Or ProductLang = "" Then Exit Sub

      Dim cmd As New GenericCommand("SQLConn")
      Dim rdr As DbDataReader
      cmd.CommandText = "Select Top 1 * from ProductsDetails Where ID=@ID and Lang=@Lang"
      cmd.AddParam("@ID", ProductID)
      cmd.AddParam("@Lang", ProductLang)
      rdr = cmd.ExecuteReader()
      If rdr.Read() Then
         Populate(rdr)
      End If
      rdr.Close()
      rdr = Nothing
      cmd = Nothing
   End Sub

   Friend Sub Populate(ByRef dr As DbDataReader)
      Populate(CType(dr, Object))
   End Sub
   Friend Sub Populate(ByRef dr As DataRow)
      Populate(CType(dr, Object))
   End Sub
   Private Sub Populate(ByRef dr As Object)
      ID = CInt(dr("ID"))
      Lang = dr("Lang")
      Name = dr("Name")
      Description = dr("Description")
   End Sub

   ''' <summary>Update Detail or Insert Detail if not there</summary>
   Public Sub UpdateOrInsertItem()
      If ID = 0 Or Lang = "" Then Exit Sub

      Dim cmd As New GenericCommand("SQLConn")
      cmd.CommandText = "Update ProductsDetails set Name=@Name,Description=@Description where ID=@ID and Lang=@Lang"
      cmd.AddParam("@Name", Name)
      cmd.AddParam("@Description", Description)
      cmd.AddParam("@ID", ID)
      cmd.AddParam("@Lang", Lang)
      'Try to update
      If cmd.ExecuteNonQuery() = 0 Then
         'if affected rows=0 cause Detail record is not there , then Insert:
         cmd.CommandText = "Insert Into ProductsDetails (ID,Lang,Name,Description) Values (@ID,@Lang,@Name,@Description)"
         cmd.ExecuteNonQuery()
      End If
      cmd = Nothing
   End Sub

   ''' <summary>Delete This Detail</summary>
   Sub DeleteItem()
      If ID = 0 Or Lang = "" Then Exit Sub

      Dim cmd As New GenericCommand("SQLConn")
      cmd.CommandText = "DELETE FROM ProductsDetails WHERE ID=@ID and Lang=@Lang"
      cmd.AddParam("@ID", ID)
      cmd.AddParam("@Lang", Lang)
      cmd.ExecuteNonQuery()
      cmd = Nothing

      Call Initialize()
   End Sub
End Class


and The Entity Collection Class for Products
Public Class Products
   Inherits CollectionBase

   ''' <summary>PageSize=0 means no paging</summary>
   Public PageSize As Integer
   ''' <summary>PageIndex=0 means no paging</summary>
   Public PageIndex As Integer
   Private mPageCount As Integer
   ''' <summary>To Get Top records if larger than 0</summary>
   Public TopRecords As Integer
   Public GetLang As String = Nothing

   Public Sub New(Optional ByVal Lang As String = Nothing)
      Call Initialize()
      PageSize = 0
      PageIndex = 0
      TopRecords = 0
      GetLang = Lang
   End Sub

   ''' <summary>Initialize collection</summary>
   Public Sub Initialize()
      mPageCount = 0
      List.Clear()
   End Sub

   Public ReadOnly Property PageCount() As Integer
      Get
         Return mPageCount
      End Get
   End Property

   ''' <summary>Gets or sets the element at the specified zero-based index</summary>
   Default Public Property Item(ByVal Index As Integer) As Product
      Get
         Return List.Item(Index)
      End Get
      Set(ByVal value As Product)
         List.Item(Index) = value
      End Set
   End Property

   ''' <summary>Adds an object to the end of the Collection</summary>
   Public Function Add(ByVal Obj As Product) As Integer
      Return List.Add(Obj)
   End Function

   ''' <summary>Select Products by Status, More Search Params can be added..</summary>
   Public Sub SelectItems(Optional ByVal Status As Product.ProductStatus = Product.ProductStatus.None)
      Call Initialize()

      Dim Top As String = ""
      If TopRecords > 0 Then Top = " TOP " & TopRecords & " "
      If PageSize > 0 And PageIndex > 0 Then Top = " TOP " & (PageIndex * PageSize) & " "

      Dim Cmd As New GenericCommand("SQLConn")
      Cmd.PageSize = PageSize
      Cmd.PageIndex = PageIndex
      'Count Query
      Cmd.CountCommandText = "SELECT COUNT(*) from Products"
      'Select Query
      Cmd.CommandText = "SELECT " & Top & " Products.* "
      If GetLang > "" Then Cmd.CommandText += ",Lang,Name,Description"
      Cmd.CommandText += " from Products"
      'Detials Table ?
      If GetLang > "" Then
         Cmd.CountCommandText += ",ProductsDetails"
         Cmd.CommandText += ",ProductsDetails"
      End If
      'Where
      Cmd.CountCommandText += " Where 0=0"
      Cmd.CommandText += " Where 0=0"
      'Tables inner join ?
      If GetLang > "" Then
         Cmd.CountCommandText += " and ProductsDetails.ID=Products.ID and Lang=@Lang"
         Cmd.CommandText += " and ProductsDetails.ID=Products.ID and Lang=@Lang"
         Cmd.AddParam("@Lang", GetLang)
      End If
      'Status?
      If Status > 0 Then
         Cmd.CountCommandText += " and Status=@Status"
         Cmd.CommandText += " and Status=@Status"
         Cmd.AddParam("@Status", Status)
      End If

      Dim DT As DataTable = Cmd.ExecuteDataTable("Products")
      mPageCount = Cmd.PageCount

      Dim p As Product
      For Each row As DataRow In DT.Rows
         p = New Product()
         p.Populate(row)
         If GetLang > "" Then
            p.Details = New ProductsDetails
            Dim d As New ProductDetail
            d.Populate(row)
            p.Details.Add(d)
            d = Nothing
         End If
         Add(p)
         p = Nothing
      Next
      DT = Nothing
      Cmd = Nothing
   End Sub

   Protected Overrides Sub Finalize()
      MyBase.Finalize()
   End Sub
End Class


and The Entity Collection Class for ProductsDetails
Public Class ProductsDetails
   Inherits CollectionBase
   ''' <summary>PageSize=0 means no paging</summary>
   Public PageSize As Integer
   ''' <summary>PageIndex=0 means no paging</summary>
   Public PageIndex As Integer
   Private mPageCount As Integer
   ''' <summary>To Get Top records if larger than 0</summary>
   Public TopRecords As Integer

   Public Sub New()
      Call Initialize()
      PageSize = 0
      PageIndex = 0
      TopRecords = 0
   End Sub

   ''' <summary>Initialize collection</summary>
   Public Sub Initialize()
      mPageCount = 0
      List.Clear()
   End Sub

   Public ReadOnly Property PageCount() As Integer
      Get
         Return mPageCount
      End Get
   End Property

   ''' <summary>Gets or sets the element at the specified zero-based index</summary>
   Default Public Property Item(ByVal Index As Integer) As ProductDetail
      Get
         Return List.Item(Index)
      End Get
      Set(ByVal value As ProductDetail)
         List.Item(Index) = value
      End Set
   End Property

   ''' <summary>Adds an object to the end of the Collection</summary>
   Public Function Add(ByVal Obj As ProductDetail) As Integer
      Return List.Add(Obj)
   End Function

   ''' <summary>Select Products Details by ID or Lang</summary>
   Public Sub SelectItems(Optional ByVal ID As Integer = 0, Optional ByVal Lang As String = "")
      Call Initialize()

      Dim Tbl As DataTable
      Dim Top As String = ""
      If TopRecords > 0 Then Top = " TOP " & TopRecords & " "
      If PageSize > 0 And PageIndex > 0 Then Top = " TOP " & (PageIndex * PageSize) & " "

      Dim Cmd As New GenericCommand("SQLConn")
      Cmd.PageSize = PageSize
      Cmd.PageIndex = PageIndex
      Cmd.CountCommandText = "SELECT COUNT(*) FROM ProductsDetails where 0=0"
      Cmd.CommandText = "SELECT " & Top & " * FROM ProductsDetails where 0=0"
      If ID > 0 Then
         Cmd.CountCommandText += " and ID=@ID"
         Cmd.CommandText += " and ID=@ID"
         Cmd.AddParam("@ID", ID)
      End If
      If Lang > "" Then
         Cmd.CountCommandText += " and Lang=@Lang"
         Cmd.CommandText += " where Lang=@Lang"
         Cmd.AddParam("@Lang", Lang)
      End If
      Tbl = Cmd.ExecuteDataTable("ProductsDetails")
      mPageCount = Cmd.PageCount

      Dim d As ProductDetail
      For Each Row As DataRow In Tbl.Rows
         d = New ProductDetail()
         d.Populate(Row)
         Add(d)
         d = Nothing
      Next
      Tbl = Nothing
      Cmd = Nothing
   End Sub

   Protected Overrides Sub Finalize()
      MyBase.Finalize()
   End Sub
End Class


A sample of usage to select a product and one Language detail:
Dim P As New Product("EN", 100)
Response.Write("ID=" & P.ID)
Response.Write("Status=" & P.Status)
Response.Write("Lang=" & P.Detail.Lang)
Response.Write("Name=" & P.Detail.Name)
Response.Write("Description=" & P.Detail.Description)
P = Nothing


A sample to select a product and all Language details:
Dim P As New Product("", 100)
Response.Write("ID=" & P.ID)
Response.Write("Status=" & P.Status)
Response.Write("Name=" & P.Detail("EN").Name)
Response.Write("Name=" & P.Detail("RU").Name)
P = Nothing


A sample to insert new product
Dim p As New Product
p.Price = 1000

Dim pd As New ProductDetail
pd.Lang = "EN"
pd.Name = "Product 1"
p.Details.Add(pd)

pd = New ProductDetail
pd.Lang = "RU"
pd.Name = "Продукт 1"
p.Details.Add(pd)

p.InsertItem()

Response.Write("id=" & p.ID)
p = Nothing
pd = Nothing


And a Sample of usage to select list of products and one language detail with paging
Dim Ps As New Products("RU")
Ps.PageSize = 10
Ps.PageIndex = 2
Ps.SelectItems()
For i As Integer = 0 To Ps.Count - 1
   Dim p As Product = Ps(i)
   Response.Write("ID=" & p.ID)
   Response.Write("Name=" & p.Detail.Name)
   Response.Write("<hr>")
   p = Nothing
Next
WriteLn("PageCount=" & Ps.PageCount)
Ps = Nothing


As Before, DAL uses a very helpful generic Command class to [Write Less & Generic Data Access Code in ADO.NET 2.0].

That is it! Hope that was helpful, Also if you have a better approach to this design pattern I would be glad to hear your ideas..

More Tech Tips! | Technology tips on web development

Mike

Mike MoreWeb developer, jQuery plugin author, social media fan and Technology Blogger.
My favorite topics are: jQuery , Javascript , ASP.Net , Twitter , Google..
<connect with="me"> </connect>

Subscribe by email

Enter your email address:

or via RSS