|
Emulating the mySql limit function in MS SQL |
There are pro's and con's to both mySql and MS SQL, one of the pro's of mySql is the limit function. This allows you to set a starting record number, and select N number of records from there. But how can we do that in MS SQL?
This is usually used for pagination functions, IE SELECT the next N records from the current count.
Firstly declare two variables, a "rows per page" and a start record.
2<cfset rowsperpage = 15>
3
4DECLARE @rowsperpage INT
5
6 DECLARE @start INT
7
8 SET @start = #startpos#
9 SET @rowsperpage = #rows#
Next we need to write the query we want, but wrap it in a parent select. Remember at this point to do all your conditional processing on the query inside the parenthesis.
2
3 (SELECT row_number() OVER (ORDER BY [intId]) AS rownum, *
4
5 FROM [table]
6 Where [table].intId = <cfqueryparam cfsqltype="cf_sql_integer" value="#arguments.intId#">) AS tempQuery
Next we can apply a where clause that filters down the results based on the two variables declared at the top of the script. This means we will only get back the required number of records, starting at our defined record number.
2Order by rownum
The whole script looks like this:
2<cfset rowsperpage = 15>
3
4DECLARE @rowsperpage INT
5
6 DECLARE @start INT
7
8 SET @start = #startpos#
9 SET @rowsperpage = #rows#
10
11SELECT * FROM
12
13 (SELECT row_number() OVER (ORDER BY [intId]) AS rownum, *
14
15 FROM [table]
16 Where [table].intId = <cfqueryparam cfsqltype="cf_sql_integer" value="#arguments.intId#">) AS tempQuery
17
18Where tempQuery.rownum BETWEEN (@start) AND (@start + @rowsperpage)
19Order by rownum
I am selecting *, but I recommend that you actually list your fields here, I tweaked it for this entry.
Not quite as nice as the mySql version tho is it:
2From table
3Limit 0,100
|
Displaying and sorting/paging tabular data using the JQuery tablesorter plugin, and query objects |
One of the more repetitive tasks a server side developer encounters is displaying the results from a query. This is traditionally in the format of a table that displays the rows of data, along with any other functionality, such as paging controls and sortable headers.
I was recently commissioned to look into building a generic table display "engine", and thought I'd investigate if there were any JQuery plugins that could do the bulk of the work for me. Ideally I didn't want to have to write a whole load of script to parse sorting variables, and detect if a limit was set on the returned record set for paging.
After some investigation I ended out using the table sorter JQuery plugin http://tablesorter.com/docs/.
This plugin allows for sortable results that you can page through, and it does not keep posting the values back and forth to the server.
Start by including the references to the JQuery libraries. I've also included references to the paging plugin, and the blue theme stylesheet.
2
3<s/cript type="text/javascript" src="/path/to/jquery.tablesorter.js"></script>
4
5<s/cript type="text/javascript" src="/path/to/ /jquery.tablesorter.pager.js"></script>
6
7<link rel="stylesheet" href="css/blue.css" type="text/css" />
Next we will create a fake query, so that we have some records to display.
2<cfset variables.qOptions = QueryNew( "id, name, color" ) />
3
4<cfset QueryAddRow( variables.qOptions ) />
5<cfset variables.qOptions[ "id" ][ variables.qOptions.RecordCount ] = "1" />
6<cfset variables.qOptions[ "name" ][ variables.qOptions.RecordCount ] = "Value 1" />
7<cfset variables.qOptions[ "color" ][ variables.qOptions.RecordCount ] = "Red" />
8
9<cfset QueryAddRow( variables.qOptions ) />
10<cfset variables.qOptions[ "id" ][ variables.qOptions.RecordCount ] = "2" />
11<cfset variables.qOptions[ "name" ][ variables.qOptions.RecordCount ] = "Value 2" />
12<cfset variables.qOptions[ "color" ][ variables.qOptions.RecordCount ] = "Green" />
13
14<cfset QueryAddRow( variables.qOptions ) />
15<cfset variables.qOptions[ "id" ][ variables.qOptions.RecordCount ] = "3" />
16<cfset variables.qOptions[ "name" ][ variables.qOptions.RecordCount ] = "Value 3" />
17<cfset variables.qOptions[ "color" ][ variables.qOptions.RecordCount ] = "Blue" />
18
19<cfset QueryAddRow( variables.qOptions ) />
20<cfset variables.qOptions[ "id" ][ variables.qOptions.RecordCount ] = "4" />
21<cfset variables.qOptions[ "name" ][ variables.qOptions.RecordCount ] = "Value 4" />
22<cfset variables.qOptions[ "color" ][ variables.qOptions.RecordCount ] = "White" />
We have to change the way we build the table code slightly, as the JQuery plugin is expecting certain field naming conventions. Give your table a class name of tablesorter. This is the style that the JQuery is watching for.
2 <thead>
3 <tr>
4 <cfoutput>
5 <cfloop list="#variables.qOptions.columnlist#" delimiters="," index="variables.index">
6 <th>#variables.index#</th>
7 </cfloop>
8 </cfoutput>
9 </tr>
10 </thead>
In the code above I am looping over the columnlist of the query to generate headings. I've had to change the headings to 'th' tags which I never normally use.
Next we can generate the table content, making sure it is inside a 'tbody' html tag. Simply loop over the query displaying all the results within td tags.
2 <cfoutput query="variables.qOptions">
3 <tr>
4 <td>#variables.qOptions.id#</td>
5 <td>#variables.qOptions.name#</td>
6 <td>#variables.qOptions.color#</td>
7 </tr>
8 </cfoutput>
9 </tbody>
10</table>
Lastly the pager plugin is looking for a div with a class of pager. Inside this div you place your paging controls, and the value of the page recordsets that you want to offset by.
2 <form>
3 <img src="addons/pager/icons/first.png" class="first"/>
4 <img src="addons/pager/icons/prev.png" class="prev"/>
5 <input type="text" class="pagedisplay"/>
6 <img src="addons/pager/icons/next.png" class="next"/>
7 <img src="addons/pager/icons/last.png" class="last"/>
8 <select class="pagesize">
9 <option selected="selected" value="10">10</option>
10
11 <option value="20">20</option>
12 <option value="30">30</option>
13 <option value="40">40</option>
14 </select>
15 </form>
16</div>
This builds a one page table display that can paginate and sort with a single refresh.
A full example of this is here.
|
Dynamically editing web content inline, using JavaScript and AJAX |
Most of us are familiar with the standard method of displaying data in a tabulated fashion, selecting a record, and populating the form that follows. What about editing the content directly into a template that mirrors the actual live version of a page?
This article examines how to edit web content directly inline, and commit it back to a server using an AJAX post request.
The main catalyst for this is that clients that use a content management system do not often have a clear image of how their content will look online. The traditional form layout for entering text does not lend itself well to representing the actual content in the format it is display in.
The aim here is to build a flexible system that allows for inline content editing, and saves it gracefully to a server based database.
I will start by saying thank you to Peter-Paul Koch. His article here (http://www.quirksmode.org/dom/cms.html) on making content editable was invaluable, and a lot of this is based on his theory.
We start by setting a value "editing" to false. This is the default for the page, as the user isn't editing anything when the page loads.
2
3if (document.getElementById && document.createElement) {
4 var butt = document.createElement('BUTTON');
5 var buttext = document.createTextNode('Save');
6 butt.appendChild(buttext);
7 butt.onclick = saveEdit;
8}
9
10function catchIt(e) {
11 if (editing) return;
12 if (!document.getElementById || !document.createElement) return;
13 if (!e) var obj = window.event.srcElement;
14 else var obj = e.target;
15 while (obj.nodeType != 1) {
16 obj = obj.parentNode;
17 }
18 if (obj.tagName == 'TEXTAREA' || obj.tagName == 'A') return;
19 while (obj.nodeName != 'P' && obj.nodeName != 'HTML') {
20 obj = obj.parentNode;
21 }
22 if (obj.nodeName == 'HTML') return;
23 var x = obj.innerHTML;
24 var y = document.createElement('TEXTAREA');
25 var z = obj.parentNode;
26 z.insertBefore(y,obj);
27 z.insertBefore(butt,obj);
28 z.removeChild(obj);
29 y.value = x;
30 y.focus();
31 editing = true;
32 getId(e)
33}
34
35function getId(e) {
36 var targ;
37 if (!e) var e = window.event;
38 if (e.target) targ = e.target;
39 else if (e.srcElement) targ = e.srcElement;
40 if (targ.nodeType == 3) // defeat Safari bug
41 targ = targ.parentNode;
42 thisTarget = e.target.id;
43
44}
45
46function saveEdit() {
47 var area = document.getElementsByTagName('TEXTAREA')[0];
48 var y = document.createElement('P');
49 // set the id back to the original value as the real one is destroyed
50 y.setAttribute('id', thisTarget);
51
52 var z = area.parentNode;
53 y.innerHTML = area.value;
54 z.insertBefore(y,area);
55 z.removeChild(area);
56 z.removeChild(document.getElementsByTagName('button')[0]);
57 editing = false;
58 // action the server request, first var is the value, second var is the id
59 saveToServer(y.innerHTML,thisTarget);
60}
61
62function saveToServer(valToCommit,fieldname) {
63 //alert(valToCommit);
64 $.post("view/appeals/act_commitChange.cfm", { newValue: valToCommit, field: fieldname, appeal: intId },
65
66 function(data){
67 alert(data);
68 });
69
70document.onclick = catchIt;
I wont go into massive depth on a line by line basis but Peter's article does break this down a lot. The premise is that there is a function catchit(), which will intercept any click events. It will then check that the event was triggered from a 'P' tag, which is our defining element for editable content. IE any P elements hold editable content. It will then remove the P html container, replacing it with a textarea, and re insert the P tags previous html content using the innerHTML JavaScript function.
In this way we can create editable inline textareas within the framework of our page.
The next step is to create a save function. The function 'saveToServer{)' take several arguments. It needs the value to commit, IE what the amended text string is, and the fieldname. Each 'P' tag has an id that I am matching to a data field. In this way if there are multiple p tags in a display they can each be attributed to a specific storage field in a database.
Because we are destroying the 'P' tag when we create the textarea we need to re assign the id to it when we save. We can do this by using the JavaScript function 'setAttribute'. The setAttribute function is used to set the value of an attribute on an object. It is typically used along with objects returned by document.getElementById to assign a new value to the object's attribute.
2y.setAttribute('id', thisTarget);
If we don't do this then the recreated 'P' tag no longer has an id attribute, so will error on any subsequent updates.
Next we use a JQuery Post function to post the values through an AJAX request.
This will post the values to the cfml CFC "commitChange.cfc", which handles them in a function.
This will allow you to perform seamless inline edits to the display layer, and commit them back to a server, so they are stored in real time.
There is an example of this here. (Minus the storing). You can track the AJAX post using a tool like charles http proxy, or firefox's firebug.
Now, to write a nice JQuery response handler to fade the returned massage in and out.
|
Android Skype applications - Skype (Beta) Vs Fring |
I often use the desktop Skype client, its not a bad application, it works and it doesn't have too big a memory footprint. I recently expanded into the arena of mobile Skype, and being an Android user I thought I'd try out the Skype applications for it.
The two big applications for the Android platform are Skype beta, and Fring.
Skype (Beta)
The name of the application is "Skype Beta". Why is it a Beta? It has been out over a year, have Skype just not finished developing it? Or is that a mild disclaimer in case it doesn't work properly?My first observation was that there are a lot of negative comments about this application in the Android marketplace, but I'm open minded, so I'm not pre judging.
I initially had problems logging into this application. This was due to the formatting of my telephone number, it is not immediately obvious that you need a country code prefix, and it flags any phone number without one, blocking the login.
The application itself is well thought out. The design is nice, and the interface uncluttered. What that does mean is that all the functionality has been moved to off screen menus, so you have to press another button just to get to the menu with the options. It would have been nice if common options were accessable through the main screen, instead that screen is blank, and pretty much a waste of real estate.
The application only display Skype contacts as well, IE you have to manually add each contact from your phone book by entering their number. There is no interface at all with your existing phone book.
The call quality is of a high enough quality that you couldn't really tell that it was not a regular mobile call, so ticks all round there. Unfortunately the messaging side of the application is a big let down. It was impossible to have a real time conversation. Messages would take a long time to arrive, in one case over three hours later than it was sent. There does not appear to be an notification settings either. I'd quite like the application to play a notification noise when I receive a new message, but this is not an option.
Skype conclusion
Overall this application was disappointing. It looks nice, but there is almost no documentation or help, and often I found myself being stopped from doing something merely because I wasn't entering data correctly. The delay in messaging makes this basically unusable for me.Fring
Fring is a third party application that piggy backs off the back of other service provider networks. When you first fire it up it prompts for any add-ons you want to install. So after installing the Skype add-on we were up and running.The first and most obvious difference is that this application connects directly to your existing phone book. It lists your current contacts, and within two clicks you can be dialling them, whether they are Skype numbers or not. (SkypeOut or regular line).
The same can be said for pre existing Skype clients. One click will allow you to select either text or chat, and then you are in the session. The call quality is of a slightly lower quality than the Skype (Beta) application but the messaging is instant. It has about the same responsiveness as the desktop Skype client, so it is very easy to have an actual conversation.
Fring will play a notification tune if you are messaged whilst the application is running in the background, so you don't miss anything.
Fring conclusion
The application can be a little process heavy on my HTC magic, but it hasn't crashed, and could act as a decent text replacement. The interface into your contact is invaluable, and it is generally a lot easier to use than the Skype application.
Conclusion
Fring just bumps the Skype (Beta) application into second place here. Maybe if Skype finished their application it would work slightly better. On the down side it is worth noting that both these application still dip into your contract minutes, even when used over a wireless network. This is quite an issue for a lot of people, and almost voids the point of Skype in the first place. Althought this seems to be an issue on the Android platform, not any other.That being said I would recommend Fring as a messaging client, especially if you are on an unlimited data plan.