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!
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!
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.
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..
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..
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.
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..
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.
Few 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:
- 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.
- Added support for right-to-left layout.
- 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.
- Few visual enhancements.
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.
The 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.
Previously, 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 :)
On 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.
Displaying 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.
of this widget that is loaded with pretty new features like posts thumbnails and transition effects..
Check features list:
- 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.
- 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.
- More Optimized: Will only use summary feeds instead of querying default feeds that return full contents!
- This widget also can work as a recent posts widget when there is no tags to aggregate.
- 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!
- A fixed list of tags can be used to aggregate.
- It can display loading text or icon until widget is loaded.
- Related posts list can be styled based on relevancy!
- You can specify either to load widget on document ready or on window load.
- Widget will be attached to container specified or it will be placed where you made the JavaScript call.
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.
for 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 :)
Meta 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):
- Null Pattern, to add a message for direct traffic visitors (no-referrer)
- 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.
- 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.
- 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..
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="▼" />
<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 ProductsImports 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 DetailsPublic 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 ProductsPublic 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 ProductsDetailsPublic 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 productDim 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 pagingDim 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..