Saturday, March 17, 2007

Asp.net : the datagrid

The datagrid controle is a powerful datacontrol but because of the amount of code to get it to work the way you want, it's a hard thing to crack for beginners. All code from this article is used in the example.

The html code



<asp:DataGrid ID="datagrid" runat="server" AutoGenerateColumns="false" ShowFooter="true"

The tag is named asp:DataGrid and like all controls it needs an id and a runat attribute.
The autogeneratecolumns attribute is needed when you want more control over the output of the data, this will be the case most of the time.
The showfooter attribute is selfexplaining and i added it because i want a row to insert new data.

OnEditCommand="datagridEdit" OnCancelCommand="datagridCancel" OnUpdateCommand="datagridUpdate" OnItemDataBound="datagridBound" OnItemCommand="datagridVarCommands" >

These are all hooks to functions for the different events.

Now we are going to style the datagrid. You can do it inside the datagrid tag but for better readability you have different style tags.

<HeaderStyle CssClass="th" />
<AlternatingItemStyle CssClass="even" />
<SelectedItemStyle CssClass="editrow" />

All style attributes can be use inside these tags but they don't have the same names as the standard html tags. There is also a FooterStyle tag.

The next thing to do is to set up the columns.

<Columns>
<asp:BoundColumn HeaderText="Id" DataField="id" Visible="false" ReadOnly="true"></asp:BoundColumn>

<asp:TemplateColumn HeaderText="Title">
<ItemTemplate><asp:Literal ID="title" runat="server"></asp:Literal></ItemTemplate>
<EditItemTemplate><asp:TextBox ID="editTitle" runat="server"></asp:TextBox></EditItemTemplate>
<FooterTemplate><asp:TextBox ID="addTitle" runat="server">></asp:TextBox></FooterTemplate>
</asp:TemplateColumn>

All columns must be inside the Columns tag.
The first column is of the type BoundColumn. This type has the least flexibility but is good for important processing data like an id or if the data can be displayed without manipulation. If you want to get the data from the column it's best to set the readonly attribute true so you can get it in every event as a string. If you set the visible attribute false the column will not be visible on the page but you can use the data that is stored in the column.

The second column is of the TemplateColumn type. Inside the template you have several tags to control the content and/or display of the column. I used ItemTemplate, that is manditory, EditItemTemplate and FooterTemplate. I repeated this for the other columns.
It is allowed to put input fields in every templatetag.

Now that we have the content of our data grid we need to add the buttons to manipulate it.

<asp:EditCommandColumn HeaderText="" EditText="Edit Info" UpdateText="Update" CancelText="Cancel"></asp:EditCommandColumn>

<asp:TemplateColumn HeaderText="">
<ItemTemplate><asp:LinkButton CommandName="Delete" Text="Delete" ID="btnDel" Runat="server" /></ItemTemplate>
<FooterTemplate><asp:LinkButton CommandName="Insert" Text="Toevoegen" ID="btnAdd" Runat="server" /></FooterTemplate>
</asp:TemplateColumn>

There are two special types of command columns. The EditCommandColumn generates three buttons for you:

- the edit button is used to change the row from regular view to edit view.
- the cancel button is used to change the row from edit view to regular view
- the update button is used to commit the changes in the edit view and return to the regular view if needed

These actions aren't programmed by default that is why you need to put the function hooks in the DataGrid tag.

The second command column is the ButtonColumn. It gives you a column with buttons who are bound to a function. It's less code but i need a column where i have delete buttons for the existing rows and an add button for inserting a row. That is why i added another TemplateColumn.

That was it for the html side of the control. If you have a long datagrid you can set the MaintainScrollPositionOnPostback true if you have the 2.0 framework or higher. If you use the 1.x framework you can use the 4GuysFromRolla solution, it worked for me.

C# code


The most important function in the code behind page is Page_Load. Here you call everything you need to display the page. Mine looks like this.

void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
Data();
}
}

Data is the function that binds the database to the datagrid control. And because the data can be different after a button click it only binds the data when there is no button click. If you don't do this your data will never change because the function is called before all the others.

public void Data()
{
try
{
conn.Open();
string sql = "select id, title,description,online from Content order by title";
SqlCommand com = new SqlCommand(sql, conn);
SqlDataAdapter myDA = new SqlDataAdapter();
myDA.SelectCommand = com;

DataSet myDS = new DataSet();
myDA.Fill(myDS);

datagrid.DataSource = myDS;
datagrid.DataBind();

}
catch (SqlException ex)
{
message.Text = ex.Message;
}
catch (Exception ex)
{
message.Text = ex.Message;
}
finally
{
conn.Close();
}
}

With the datagrid.DataSource variable you transfer the database query to the datagrid and datagrid.DataBind() does exactly what it says.

Now is you data connected with the datagrid but if you would run the code now you would see that only the BoundColumn has values. The template columns need some more attention.

public void datagridBound(Object sender, DataGridItemEventArgs e)
{
// get bound data row
DataRowView row = (DataRowView)e.Item.DataItem;

switch(e.Item.ItemType)
{
// @@@@ EDIT TEMPLATE
case ListItemType.EditItem:
((TextBox)e.Item.FindControl("editTitle")).Text = row["title"].ToString();
((TextBox)e.Item.FindControl("editContent")).Text = row["description"].ToString();
if ("1" == row["online"].ToString()) { ((CheckBox)e.Item.FindControl("editOnline")).Checked = true; }
break;
// @@@@ REGULAR TEMPLATES
case ListItemType.AlternatingItem:
case ListItemType.Item:
((Literal)e.Item.FindControl("title")).Text = row["title"].ToString();
((Literal)e.Item.FindControl("content")).Text = row["description"].ToString();
string online = "No";
if("1" == row["online"].ToString()){ online = "Yes"; }
((Literal)e.Item.FindControl("online")).Text = online;
break;
}

}

The datagridbound function takes care of the data for the regular templates and for the edit template. This way you have all the bound data manipulations in one function.
There is another way to bind the data in the templatetags.

<ItemTemplate><%# DataBinder.Eval(Container, "DataItem.id")%></ItemTemplate>

To manipulate this data you need to create a function, if you need to do this for several columns it can add a lot of code.

Next we have the event functions

public void datagridEdit(Object sender, DataGridCommandEventArgs e)
{
datagrid.EditItemIndex = (int)e.Item.ItemIndex;
Data();
}

public void datagridCancel(Object sender, DataGridCommandEventArgs e)
{
datagrid.EditItemIndex = -1;
Data();
}

These functions are for the edit and cancel button from the EditCommanndColumn.

public void datagridUpdate(Object sender, DataGridCommandEventArgs e)
{
message.Text = "<p>Updated row:</p><p>Id : " + e.Item.Cells[0].Text + "</p>";
message.Text += "<p>Title : " + ((TextBox)e.Item.FindControl("editTitle")).Text+"</p>";
message.Text += "<p>Content : " + ((TextBox)e.Item.FindControl("editContent")).Text + "</p>";
string online = "No";
if (((CheckBox)e.Item.FindControl("editOnline")).Checked) { online = "Yes"; }
message.Text += "<p>Online : " + online + "</p>";
Data();
}

This function supports the update button. Normally you will put your database code to update the databasefields.

public void datagridVarCommands(Object sender, DataGridCommandEventArgs e)
{
switch(e.CommandName)
{
// !!!! DELETE
case "Delete":
message.Text = "<p>Delete row</p><p>Id : " + e.Item.Cells[0].Text + "</p>";
break;
// !!!! ADD
case "Insert":
message.Text = "<p>Insert row:</p>";
message.Text += "<p>Title : " + ((TextBox)e.Item.FindControl("addTitle")).Text + "</p>";
message.Text += "<p>Content : " + ((TextBox)e.Item.FindControl("addContent")).Text + "</p>";
string online = "No";
if (((CheckBox)e.Item.FindControl("addOnline")).Checked) { online = "Yes"; }
message.Text += "<p>Online : " + online + "</p>";
break;
}
}

This last function is used to handle the delete and add buttons.

Conclusion


There is a fair amount of code necessary to come to a working datagrid but there are also parts where the datagrid control does the legwork. You can do a lot more with the datagrid control but the code in this article will come back most of the times.

The full code can be dowloaded.

Friday, March 16, 2007

Menu with javascript elements

There are a lot of javascript menu enchanters but the one thing seems to be missing the ability to degrade the menu.

I made an example that generates a part of the menu in javascript and a part that is accessible all the time. I wanted to have a different design for the non javascript menu.

The code can be shorter but i wrote it out so it's easier to understand.

Saturday, March 03, 2007

Google services going down?

In one day i got accused by google search and blogger of being a robot. Now i know i like technology but there aren't any computerized parts in my body, yet.

At work i was looking for code snippets and suddenly i got a page telling me my computer was infected and i had to fill in the captcha and then i could continue.

When i came home i wanted to write a poem on my other blog but it was flagged as a spam blog. I had to verify again that i was a human but to make it harder the captcha kept disappearing in firefox. I had to do the verification with internet explorer.

I read in the feeds people were having trouble with gmail as well. My gmail is a secondary email so i don't have a problem if it breaks down but people who depend on gmail as their master mail center must have had a hard time.

I think it's a conspiracy to promote the image captcha. People are coming up with other ways to protect their sites against bots and google can't have that. Then they can't do anything with the ocr software they bought or developed, i'm not sure. Tricks to outsmart the king of bots is a big no-no.

Anyway i'm declared human again by google search and blogger, sci-fi has become reality, now i can implant many rfid tags and a bioport for games.

Saturday, February 24, 2007

Lay-out html

i had to do a cross browser design and i adapted some new techniques and learned some new things.

The html code


The 'new' thing on the block for css layout are the conditional statements. They are comments that acts like if statements. Browsers don't render css the same and when you use these statements you can keep your css files clean of hacks. Much of the rendering quirks have to do with positioning the elements so you can create a general css file for the cross-browser styles. Because some of the rendering is different in IE6 and IE7 you can create a general IE file. So lets look at the code for it now, the css examples will follow later.

<link rel="stylesheet" type="text/css" href="css/default.all.css">
<!--[if IE]> <link href="css/default.ie.css" rel="stylesheet" type="text/css"> <![endif]-->
<!--[if lte IE 7]> <link href="css/default.ie7.css" rel="stylesheet" type="text/css"> <![endif]-->
<!--[if lte IE 6]> <link href="css/default.ie6.css" rel="stylesheet" type="text/css"> <![endif]-->
<!--[if !IE]>--> <link href="css/default.non.ie.css" rel="stylesheet" type="text/css"> <!--<![endif]-->

The conditional comments can be hidden, like the IE stylesheets, or visible like the non iE stylesheets.

The css code


Another thing i discovered is the peek-a-boo bug when there are floating divs inside another div. If you want to display a border for the container div the border disappears and appears magically if you scroll the page. Of course this only happens with IE.

There are two solutions. One is to set the position to relative but i experienced that if there is a hover-to-showmenu that works with absolute positions the shown items appear to be under the relative positioned element.
Here is where solution 2 kicks in. With the IE only zoom style you can get rid of the peek-a-boo bug without breaking javacripted behaviour if you set it to 1.

It's a bit of common knowledge but maybe someone can benefit from this.

Saturday, January 27, 2007

From php to c#and asp.net

I learned asp.net for a few months three years ago and now i have to do a website for my work so i had to get acquainted again with the programming logic and c# syntax. I use c# because i can't say goodbye to the curly brackets blocks, Maybe python can make me abandon them but not VB.net.

The basics


The first thing i needed to know is how to include files because in php the master-content pages is set up by including files. Because the server only has the .net 1.1 framework i couldn't work with the master-content pages. This is where i first noticed a big difference between php and asp.net/c#. Where php is an embedded programming language, asp.net is the templating language for c#. You can go the php way and embed the c# code into your html code but if you use the asp.net controls and ids for other html elements you can keep your html clean enough so that a designer can understand what you are doing. All the html ids maybe a torn in the eye of css architects and javascript coders but it's workable.

But now back to the include files syntax. It's a weird html comment but it's easier than in php

<!--# file="c:\httpdocs\site\\test.aspx" -->

<!--# virtual="/site/testpage" -->

The first example is the counterpart of the include function from php even with the same double quote behaviour. The second example is nicer because it's relative to the root directory. In php i had to provide that myself by using a constant.

The next thing was how to get the php $_POST and $_GET globals. That was easy enough Request.Form["test"] gets you the posted form values, Request.QueryString["test"] gets you the url parameters. The Request object is like the $_SERVER global in php but the $_GET and $_POST are a part of it too which is cleaner than in php.

Now i got the basics i can make things work, i thought. c# is a strong typed language so you have to know which sort of content your variable will hold. You can guess Request.QueryString["test"] is a string but to compare it with a numeric variable you need to convert it.

int test = Convert.ToInt32(Request.QueryString["test"]);


The database


Now for the database connection. In php code there is morethan one way to do this but most of the database manipulation code now is object oriented and so is the c# code. The difference in c# is you have to use parametrized queries.

<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.Odbc" %>
<script language="c#" runat="server">
// variables
int number = 1;
string query = "SELECT name FROM testtable WHERE id = ?";
// prepare connection
OdbcConnection conn = new OdbcConnection("Driver={Mysql Odbc 3.15 Driver}; Server=localhost; Database=test; User=user; Password=password; Option=3;");
// prepare database connection
OdbcCommand com = new OdbcCommand(query, conn);
// add value to query string
com.Parameters.Add("",OdbcType.BigInt, 20).Value = number;
// start error handling
try
{
// open connection
conn.Open();
// get database result
OdbcDataReader reader = com.ExecuteReader();
// process database result
while(reader.Read())
{
textboxName.Text = reader.GetString(0);
}
// because it's a single result it can be shorter
textboxName2.Text = com.ExecuteScalar();
reader.Close();
}
// database error
catch (OdbcException ex)
{
labelMessage.Text = ex.Message;
}
// general errors
catch (Exception ex)
{
labelMessage.Text = ex.Message;
}
finally
{
conn.Close();
}
</script>
<html>
<head>
<title>test</title>
</head>
<body>
<p><asp:Label id="labelMessage" runat="server" /></p>
<p><input type="text" id="textboxName" runat="server" /></p>
<p><input type="text" id="textboxName2" runat="server" /></p>
</body>
</html>

To summarize the previous code.

  • Define classes needed for database manipulation

  • C# code

    • Prepare database manipulation

    • Display database result



  • Html/asp.net code



I will give you some rest now and i hope i helped other php programmers to take their first steps in asp.net/c#.

Sunday, January 14, 2007

New installation, different taskbar

I bought a new laptop and as a windows power user i adjust many things to make my experience easier. I found a few new things, for me, this time.

I never cared too much about all the toolbars in the taskbar except for the quicklaunch one. As an experiment i added the address toolbar. I found out it was good for launching websites, even with firefox as default browser, and for opening directories/folders without the trail of double clicks to get to the folder. As a bonus the address field remembers previous inputs.

I also tried if cmd would run and it did so i searched for a way to start other programs from the address bar. The solution i come up with is simple and expandable.

I created a batch file, a text file with the extension .bat, with only one line.

START c:\start\%1

The start command lets you run the program. The folder is one i made to put all my program shortcuts in. %1 is the first parameter of the batch file. I saved the file as start.bat in the c:\windows\system32 folder.
In the start folder i made a shortcut to 7-zip with that name so i can type in the address field 'start 7-zip' and the program starts. The only thing you have to do is to add shortcuts with the name you prefer to start other programs.

I like visual aids to search the window i'm looking for. In firefox i use the ctrl tab preview add-on and for programs i use the Microsoft Alt-Tab Replacement powertoy. I had downloaded the Visual Task Tips program but i hadn't installed it before. This shows a thumbnail screenshot of the program if you hover over the program in the taskbar. Because i'm using the address toolbar i put the taskbar on top which made the visual task tips more visible too. Now together with the Taskbar Shuffle program the taskbar has become more than a collection of clickable things.

Update : i changed from alt-tab replacement powertoy to TaskSwitchXP because the screenshots are bigger and it selects windows that aren't in the taskbar too.

Monday, January 01, 2007

Beginning python : function

Functions are another basic part of any programming language, without it you have to rewrite/copy all your code. And knowing how bad programmers are at copying it's not a good idea. And again python beats php for this part. functions are called definitions in python.

Building functions



function name($arg1,$arg2='optional',$arg3='optional'){
echo $arg1.' '.$arg2.' '.$arg3;
}

A simple function with 2 optional arguments that shows the values of the arguments. Now the same function in python.

def name(arg1,arg2='optional',arg3='optional'):
    print arg1, arg2, arg3


The differences in syntax are:

  • def instead of function

  • open function with a : character

  • indentation of codeblocks is necessary

  • the , character instead of the . character

  • close function with a white line


You also notice that you don't have to use a string with a space to separate the values of the arguments because the print function processes the white space in the code.

Using functions



name(1);
name(1,2,3);
name(1,'optional',3);

Above you can see a few function calls for the php function. The last call shows a pain in php. You have to know the default value of an argument to access an argument later in the list. To break this behaviour you can put your optional arguments in an associative array and sort them out in the function.

function name($arg1,$array){
echo $arg1;
$arg2 = ' optional';
$arg3 = ' optional';
if(count($array) == 0){
echo $arg2.$arg3;
}else{
foreach($array as $key => $value){
if($value != ''){
switch($key){
case 'arg2': echo $arg2; break;
case 'arg3': echo $arg3; break;
}
}
}
}
}

As you can see this extends the function code quite a lot and in this example you can only use it if the optional arguments don't depend on each other. Now lets look at some python magic.

name(1)
name(1,2,3)
name(1,arg3=3)

It are the same function calls and as you can see the last one sets the argument using a keyword/value pair. You can call all arguments like that but once you start setting arguments by keyword/value pairs you can't use the value only setting any more.

name(1,arg2=2,3)

This example will cause an error. Wrong keywords also raise an error.

The setting function arguments by using key/value pairs is a real programming relief.

First of all you don't have the remember the default values of optional arguments when you want to set an argument later in the list, you don't even have to add it to your function call.

Second reason : you don't have to remember the place of arguments in the list. Remembering the argument names is easy if you use consistent argument names, remembering where you put the arguments in the list is harder if you made the function six months ago and haven't used it a lot.

Third reason : you can use an associate array (dictionary in python speech) or even an array (list in pythonese) to set the arguments.
A dictionary is build by a key/value pair(s) surrounded by curly brackets.

listtest = [1,2]
name(*listtest)
dicttest = {'arg1': 1, 'arg2': 2}
name(**dicttest)

You notice the use of the single and double asterisk to identify the datatype to unpack it in the function.
This is great because you don't have put different function calls in if statements if the arguments for the function call are different. You can build different argument dictionaries or lists and call the function in the end. This makes the code more readable.

Sunday, December 31, 2006

Mimic python

After the article yesterday i decided to mimic the python behaviour for strings and arrays.
Below you find the function to get parts from a string or an array. I added a '1,5,4' limiter to get specific and/or loose parts.

function getStrArr($var,$limiter){
if(is_numeric($limiter)){
// get one value
if(strpos($limiter,'-') === false){
return (is_array($var))?$var[$limiter]:substr($var,$limiter,1);
// get last value(s)
}else{
return (is_array($var))? array_slice($var,$limiter) : substr($var,strlen($var)+$limiter) ;
}
}else{
if(strpos($limiter,',') === false){
if(strpos($limiter,':') == 0){
$lim = str_replace(':','',$limiter);
// get part from the begin
if(strpos($limiter,'-') === false){
return (is_array($var))?array_slice($var,0,$lim):substr($var,0,$lim);
// get part from the end
}else{
return (is_array($var))? array_slice($var,$lim) : substr($var,strlen($var)+$lim);
}
}else{
$temp = explode(':',$limiter);
if($temp[1] == ''){
// exclude part from begin
if(strpos($limiter,'-') === false){
return (is_array($var))?array_slice($var,$temp[0]):substr($var,$temp[0]);
// exclude part from the end
}else{
return (is_array($var))? array_slice($var,0,count($var)+$temp[0]) : substr($var,0,strlen($var)+$temp[0]);
}
}else{
// get self defined part
$min = $temp[1]-$temp[0];
return (is_array($var))? array_slice($var,$temp[0],$min) : substr($var,$temp[0],$min);
}
}
}else{
// get pieces
$temp = explode(',',$limiter);
if(is_array($var)){
$arr = array();
foreach($temp as $t){ $arr[] = $var[$t]; }
return $arr;
}else{
$str = '';
foreach($temp as $t){ $str .= substr($var,$t,1); }
return $str;
}
}
}
}

Saturday, December 30, 2006

Beginning python : string and array

I wanted to climb up the programmer hierarchy so i decided to look at some python tutorials, not that i'm near to be a good OO php programmer but i found out if you aim high you get better doing the things you think you understand.

There are already a few things that caught my attention. At first the webdevelopment seems to look awfully like perl. In a tutorial i found they just printed the the html but i think i will find better ways when i explore python further.

Beside the obvious differences like syntax i found it refreshing there were similar functions for strings and arrays, they are in a datatype group called sequences. I will switch from python to php to show similar output

# define string and array variable
string = 'test'
array = [1, 2, 3, 4]


// define string and array variable
$string = 'test';
$array = array(1, 2, 3, 4);


# get size
len(string)
len(array)


// get size
strlen($string);
count($array);


# get one value
string[2]
array[2]


// get one value
substr($string,2,1);
$array[2]


# get last value
string[-1]
array[-1]


// get last value
substr($string,strlen($string)-1);
$array[count($array)-1];


# get multiple values start in the beginning
string[:2] # display the first 2
array[:2]
string[2:] # display everything except the first 2
array[2:]


// get multiple values start in the beginning
substr($string,0,2); // display the first 2
array_slice($array,0,2);
substr($string,2); // display everything except the first 2
array_slice($array,2);


# get multiple values start in the end
string[:-2]
array[:-2]
string[-2:]
array[-2:]


// get multiple values start in the end
substr($string,strlen($string)-2);
array_slice($array,-2);
substr($string,0,strlen($string)-2);
array_slice($array,0,count($array)-2);


# get part of variable
string[1:3]
array[1:3]


// get part of variable
substr($string,1,2);
array_slice($array,1,2);


# add value
string + ' test'
array + 5


// add value
$string .= ' test';
$array[] = 5;


# copy variable with itself
2*string
2*array


// copy variable with itself
$string .= $string;
foreach($array as $value){ $array[] = $value; }

As you can see there is more php code necessary and it involves different functions as well. There is also a loop necessary in php to mimic the python code.
You can also see a difference getting a part of the variable in python and php. In python you limit the part by the value key and in php you limit the part by how many values you need.

Because strings and arrays are a basic in every language it has to be as easy as possible to work with them.

Monday, December 25, 2006

Mysql last inserted id

In a few articles on mysql the function LAST_INSERT_ID() is used to extract the last inserted id but i experienced a problem with it. When i insert multiple rows in one query the LAST_INSERT_ID displays the first inserted id and not the last.

INSERT INTO table (field1,field2) VALUES (value1,value2),(value3,value4)

The function is also restricted to the table field that has an AUTO_INCREMENT value. To avoid the problem and make it more flexible i created a maxFieldOfTable function.

function maxFieldOfTable($table,$field = 'id'){
$result = mysql_query('SELECT MAX('.$field.') FROM '.$table);
return (!$result)? mysql_error():mysql_result($result,0);
}

Sunday, December 24, 2006

Simple php/javascript in place editing

This last few weeks i learned more about things i consider basic so that's why i want to give some working examples of it.

The first thing i want to get some attention to is the button tag. I read an article in the forgotten html tags series about the button tag. In the article the design advantages where emphasised but there are programming advantages too. The biggest advantage is the separation of the value and the title.

<input type="submit" name="button" value="Title" />
<button type="submit" name="button" value="1">Title</button>

When you have an input tag the posted value is the value that you have to display on the button. With the button tag you can put any value you want to post in the value attribute.

When i made forms where a row of data needed to be changed i used a radiobox with the id of the row which needed to be checked before you could click a button. With the button tag it's possible to only have to click on the button.

Php code



Now that we got that behind us lets look at the php code of the in place editing

foreach($data as $row){
$text = $row['field1'];
$buttonvalue = $row['id'];
$buttonname = 'update';
$buttontitle = 'Update';
if((isset($_POST['update']) && $_POST['update'] == $buttonvalue) ||
(isset($_POST['updateok']) && $check == 0 && $_POST['updateok'] == $buttonvalue)){
$text = '<input type="text" name="text" id="text" value="'.$text.'" />';
$buttonname = 'updateok';
$buttontitle = 'OK';
}
?><tr><td><?php echo $text; ?></td><td><button type="submit" name="<?php echo $buttonname; ?>" class="<?php echo $buttonname; ?>" value="<?php echo $buttonvalue; ?>"><?php echo $buttontitle; ?></button></td></tr><?php
}

This is the code needed to display the form input to change the text. The data variable is a result from a query that is caught in an array. To make the text changeable i rely on the button name and the value send when the button with the update class is clicked. The update code is located in a separate file because of the javascript functionality.

if(isset($_POST['updateok'])){
// mysql : update table set field1='$_POST['text']' where id= $_POST['updateok'];
$check = (mysql_affected_rows() == 1)? 1:0;
if(isset($_POST['js'])){ echo $check; }
}

As you could see in the first code fragment there is a second statement when the text has to be form input and that's when it wasn't possible to update the text. Most of the time that is due to a temporary programming blindness- not correct table, set a character to much or to little, ... - but it can be useful to report problems once the site is live. The only thing i needed to add to make it work in javascript is the output of the check variable.

Javascript



This was the easy part. Now we are going to climb steep coding hills. I use the jQuery library to make the javascript coding easier. The javascript code can be split up in two parts; the changing of the text to form input and the updating of the text. The changing of the text can also be split up in two parts. The first part is the check for changeable text and close it if there is a changeable text found.

if($('button.updateok').size() == 1){
$('button.updateok').html('Update').attr({class:'update'});
var text = $('#text').attr('value');
$('#text').parent().html(text);
}

People who are familiar with jQuery see a few simple lines but i will write it out.
If a button tag with the class updateok is found the innerhtml of the button will be changed to Update and the class will be changed to update. The variable text catches the value attribute from the element with the id text, this variable will be placed in the innerhtml of the parent tag that contains the element with the id text.
The second part is changing from the text to form input.

$(this).html('OK').attr({class:'updateok'});
var text = $(this).parent().prev('td').html();
$(this).parent().prev('td').html('<input type="text" name="text" id="text" value="'+text+'" />');

The code is similar so i won't write it out. The only thing that has changed is the this variable. I can work with it because the code is called in the function that is responsible to intercept the click event. You will see it later.
Now we are ready for the updating code

var id = $(this).attr('value');
var textt = $(this).parent().prev('td').children('#text')[0];
var text = $(textt).attr('value');
var self = this;
$.ajax({
type: 'POST',
url: 'update.php',
data: 'updateok='+id+'&text='+text+'&js=1',
success: function(msg){
if(msg == 1){
$(self).html('Update').attr({class:'update'});
$(self).parent().prev('td').html(text);
}else{
alert('Problem changing field.');
}
}
});

The first part of the code is lifting the necessary values off the page to put then in the server request. The text value is found by starting from the button (this), going up the dom tree (parent), find the previous tablecell (prev('td')) and look in the tablcell for the first element with the id text (children('#text')[0]). This is caught in a temporary variable that is used to extract the value. The variable self is used in the callback of the request. The request has a this variable of his own.

The request itself happens inside the ajax function. The hash of the function takes care of the different parts. The succes key of the hash is called when the server returns an ok message. To get the returned values from the server you have to catch it in a function argument (msg) and because i echoed the check php variable all i had to do is look for the 1 value. If it's 1 then the text and the button change again.

If you have read so far you deserve to see the full javascript code.

$(function(){
$('button.update').click(function(){
if($(this).attr('class') == 'updateok'){
var id = $(this).attr('value');
var textt = $(this).parent().prev('td').children('#text')[0];
var text = $(textt).attr('value');
var self = this;
$.ajax({
type: 'POST',
url: 'update.php',
data: 'updateok='+id+'&text='+text+'&js=1',
success: function(msg){
if(msg == 1){
$(self).html('Update').attr({class:'update'});
$(self).parent().prev('td').html(text);
}else{
alert('Problem changing field.');
}
}
});
}else{
if($('button.updateok').size() == 1){
$('button.updateok').html('Update').attr({class:'update'});
var text = $('#text').attr('value');
$('#text').parent().html(text);
}
$(this).html('OK').attr({class:'updateok'});
var text = $(this).parent().prev('td').html();
$(this).parent().prev('td').html('<input type="text" name="text" id="text" value="'+text+'" />');
}
return false;
});
});

The first line is called when the document is loaded. The second line is the click event interceptor function. The third line is a check if the button has the updateok class. This may seem odd because in the click function you look for the button with the update class but because the first line is only called when the page has loaded it keeps seeing the original state. Only after the click event it finds the javascript manipulated elements and their attributes.
In the end the return false statement is added so that the normal behaviour of the button is cancelled.

Conlusion



Now you have two layers of functionality that react the same and do the same with a minimal amount of code. For javascript i used more code than in php because the posting of the form in php takes work out of my hands. This code isn't finished because there is no input control but that can be added easily.

An example can be found at : Simple php/javascript in place editing

Saturday, December 16, 2006

Programmers logic

In the years i'm programming webforms i always had the instinctive idea i should penalize the user of the forms with an error message and redirecting the user back to the form. I know now that is a wrong line of thinking.

It is wrong because of two reasons
- Users need to fill in some of the fields again
- The need to program the error messages and return of entered data

In the era usability is not a buzz word but a rule to live by it's plain wrong to let the user input data again when it's not necessary.

A scenario : we have three radioboxes; good, bad, neutral. Connected to the bad radiobox are three checkboxes; very, very, very (I didn't think a lot about this).

My normal behavior would be to check if one of the checkboxes was checked. If one is look if the bad radiobox was checked. If it isn't back to the form clearing the checkboxes and radioboxes and an error message on top. This is brutal behavior. At least i could check the bad radiobox and let the checkbox stay checked but then it would be rude to redirect to the form because the only thing i do then is saying how stupid the user is.

Adding error code is another thing. It is annoying because most of the time it is basically the same code. It's the design of the site or the demand of the client that forces you to write the code in a not generic way. Because it is a user input there is always a possibility of a form of an hack attack so you have to check that too before returning the value.

Taking the scenario from above you can add a css class to the label or a message next to the label or a border around the check- and radioboxes, ... .

The best way to deal with the given scenario is to check if a checkbox is checked. If it is set the radiobox value to the bad value and let the user think they did everything good.

You can't do that with every inputfield you have. You have to balance the user/programmer decisions about the input. If some data is necessary to continue you have to set up errors but otherwise make the form filling labor as smooth as possible.

The new beginning of my programming blog

I've been neglecting this blog for a while so i'm starting again and what starts a boring blog better than humor

Saturday, October 21, 2006

Plugin updates

I rewrote the mp3player plugin and changed the page. The code is no longer a download. You can copy and paste it from the page. The code is much cleaner now.

I made some small changes in the texrep plugin. Check the updates for more information.
There are still some bugs left but i'm working on them.

Sunday, October 15, 2006

New jquery plugin : texrep

I worked on a new jquery plugin: texrep. It's short for textreplacement. It's just a funny name.

Texrep replaces a text with an image using a font file.

You can find the code and examples at http://dlinck.d.googlepages.com/jquerytexrep.

Tuesday, October 10, 2006

jquery mp3player update

I moved the player code to my jquery googlepage

I added more options like playing a file on startup and choice of flashplayer insertion.

I will set up som examples tomorrow.

Monday, October 09, 2006

Structured css file

There is a lot to find about how to structure your css files but you have to find the best method for yourself.

With comments you can't go wrong. I use them like this

/*------------ generic --------*/

/*------------ div -----------*/

/*------------ span -----------*/

/*------------ title -----------*/

/*------------ paragraph -----------*/

/*------------ link -----------*/

/*------------ list -----------*/

/*------------ image -----------*/

/*------------ form -----------*/

/*------------ table -----------*/

This way you can find the css style by the function of the html tag and still add extra comment. To make it more readable I add the tag in front of the id or class selector when i have to use a parent element.

/*------------ div -----------*/
#content
    { width: 80%; }
#navigation
    { width: 20%; }
/*------------ link -----------*/
div#content a, div#navigation a
    { text-decoration: none; }
div#navigation a
    { font-weight:bold; }

The following advise you can see in the previous example. by indenting the actual style and putting it on one line you have a visual guidance and a compact css file. An additional advantage of seperating the selectors from the style is that you can have a line of selectors where the style is fully visible underneath. Of course you must be aware not to use to many selectors. In the example the first link selector can be

/*------------ link -----------*/
a
    { text-decoration: none; }


This works for me but different methods aren't wrong.

Monday, September 11, 2006

Good practice

There is a lot said about good practice when programming and there are a few things i use.

- Produce reusable code

Even if you are sure the code you write is never going to used on another site write it as it will be used for other sites. Reusable code is a lot cleaner and more flexible than improv code. This makes your site faster.

- Only the code that displays html must maintain html.

Get as much html out off php classes and javascript functions. If there is no way to remove the html out of the code keep it as simple as possible. If a css class is needed or another attribute make it an argument so it can be adjusted without crawling through the function code.

- Javascript interfaces need server language script

When you are coding in javascript sometimes you can forget it can be turned off. You can make nice things but when users can't do anything on your site without javascript you never reach all the people that want to use your site. If there is html that has no purpose without javascript it's site fat.

- Use html wisely

Because div and span tags are the most generic tags you can use them to replace all tags. Use html tags according to their purpose. You can bend their purpose a bit like using a list to build a menu that looks like a table. Use an id attribute only once per page.

- Make css readable and cascading

When you create css based on html you are going to get into trouble sooner or later. If you put everything in a class or an id your css file is going to be lengthy and you are going to have to use multiple classes if you want to add javascript functionality to an element. Use an id to style included elements. Split your css file into sections using comment lines.

These are a few tips. I hope they work out for you too.

Sunday, September 10, 2006

From server to client

I think most people who are used to program with server languages like php and perl have trouble ajusting to javascript as a partner language instead of a supporting language.

To give an example. I've worked on Smooth paginator. The idea behind it is simple. Group pages together by sets to minimize the place taken by the page links. Although it's nice work the one thing that's bothering me is the fact that the sub-group is generated by the php code therefore it is shown when javascript is turned off, The subgroup has no use then. The subgroups need to be generated by javascript to degradate gracefull.

If you plan to migrate from a server script to a more client script approach you have to keep in mind javascript can be turned off or not read by the browser. Try to back-up as much client functionality with server functionality. The way i do it is by creating everything in a server script language and then i'm creating the client script whitch uses methods from the server script through ajax calls. This way you don't have to program the same funtionality twice and you have the new way of handling user actions.
If it's difficult to mimic the javascript behaviour make sure the functionality is not essential for the use of the application.

I've adjusted the smooth paginators code to generate the sub-groups but it has to be approved by the creator of the code. But i'll think he isn't going to be difficult about it because he switched from version .02 to .03 using my rough javascript code. That's surely nothing to be proud of, the improved code is.

Tuesday, September 05, 2006

Blogger navbar

The blogger branded bar hides itself as soon as the mouse moves in an area outside of the bar. This are four lines of jQuery code.

$(document).mousemove( function(e) {
ym = (e.y || e.clientY);
if(ym < 32){ $("#b-navbar").attr("class","show"); }else{ $("#b-navbar").attr("class","hide"); }
});

First we add the mousemove event to the document so that it can monitor the cursor. Than we define the place of the cursor in relation with the top of the visible website.
The bar is 32 (firefox)/ 34 (IE) pixels so i took the lowest number. If the mouse is below that height the class show will be added to the bar whitch the blogger people gave the id b-navbar. If the mouse is above that height the class show will be added.

My first impulse was to use the hide and show functions of jquery but they didn't respond so i made a hide and show css class where i change the visibility.

You could put the search box on your page so it stays accessable all of the time by putting the form code somewhere into your template.

<form id="b-search" name="b-search" action="http://search.blogger.com/"><input type="text" id="b-query" name="as_q" />
<input type="hidden" name="ie" value="UTF-8" />
<input type="hidden" name="ui" value="blg" />
<input type="hidden" name="bl_url" value="xwerocode.blogspot.com" />
<input type="image" src="http://www.blogger.com/img/navbar/2/btn_search_this.gif" alt="Search This Blog" id="b-searchbtn" title="Search this blog with Google Blog Search" onclick="document.forms['b-search'].bl_url.value='xwerocode.blogspot.com'" />
<input type="image" src="http://www.blogger.com/img/navbar/2/btn_search_all.gif" alt="Search All Blogs" value="Search" id="b-searchallbtn" title="Search all blogs with Google Blog Search" onclick="document.forms['b-search'].bl_url.value=''" />
</form>



I skimmed the divs and other things that where in the original form to make the form more readable. Ofcourse you have to change xwerocode.blogspot.com to your url or you could search my blog on yours.
If you want to change the color of the form just use the style attribute in the form tag.
You can change the type of the button from image to button to have more lay-out options but don't forget to add a value to the "search this blog" button.