In AJAX 1.0 Extensions a developer can create an AJAX
Control, an AJAX Extender Control, and an AJAX class that can encapsulate the
functionality of a feature to be added to the Web application. In this article
we will create an AJAX class which will include all the methods required for
the inline-edit to work properly on a Web Form. However, this AJAX class can be
easily converted into an AJAX Extender control.
The inline-edit script starts by defining the constructor as
shown in Listing 1.
Listing 1
// Register a new Namespace
Type.registerNamespace("bhaidar.CodeSuite.AJAX");
// Define the constructor of the InlineEdit class
bhaidar.CodeSuite.AJAX.InlineEdit = function (uiElement) {
// Call base class just for completeness
bhaidar.CodeSuite.AJAX.InlineEdit.initializeBase(this);
this._uiElement = uiElement;
this._postPage = "InlineEditHandler.aspx";
this._otherVars = "";
this._editMode = false;
this._spanToEdit = null;
// ChangeDisplay Delegate
this._changeDisplayDelegate = Function.createDelegate(this,
this.ChangeDisplay);
}
As you can see, a new namespace has been created then the
constructor of the inline-edit class is defined. The AJAX class is based on a
UI element that is passed through the constructor. This UI element is the
element that will be converted into an inline Textbox or TextArea as we will
see later in this article.
The _postPage private member is the page that will be used
to post to it and it will handle saving the changed text either to an XML file
or database table.
The _editMode private member represents the status of the UI
element whether it is in an edit mode or read mode.
The _spantoEdit private member holds the ID of the span HTML
tag on the page.
Finally, the _changeDisplayDelegate is created to handle
execution of a function, called ChangeDisplay, in the same context of the AJAX object that executed it.
Now it is time to add functions and properties to the
prototype of the AJAX class. Listing 2 shows all the properties added to the AJAX class.
Listing 2
// Define uiElement Property
// This property is used to hold the ID of the UI
// control that is to be edited inline
get_uiElement:function() {
// Get
return this._uiElement;
},
set_uiElement:function(value) {
// Set
this._uiElement = value;
},
// Define postPage Property
// The page to post to that will handle
// saving the text typed on the page into a
// data store
get_postPage:function() {
// Get
return this._postPage;
},
set_postPage:function(value) {
// Set
this._postPage = value;
},
// Define otherVars Property
// This variable is used to supply additional
// variables to the postPage
get_otherVars:function() {
// Get
return this._otherVars;
},
set_otherVars:function(value) {
// Set
this._otherVars= value;
},
// Define editMode Property
// This variable is used to keep track of
// editing mode of the _uiElement
get_editMode:function() {
// Get
return this._editMode;
},
set_editMode:function(value) {
// Set
this._editMode = value;
},
// Define spanToEdit Property
// This property is used to hold the span control
// that is going to be edited
get_spanToEdit:function() {
// Get
return this._spanToEdit;
},
set_spanToEdit:function(value) {
// Set
this._spanToEdit = value;
},
There is nothing special in the above code. For each private
member a set/get function has been added. As you know, in AJAX there are no
properties and hence the need to create two separate functions, one for the set
and one for the get.
The brain of the inline-edit AJAX class is the EditBoxInit
function that is shown in Listing 3.
Listing 3
// This method parses the HTML page, get all spans with class "editText",
// then configures each span to be clickable and editable
EditBoxInit:function ()
{
if (!document.getElementsByTagName){ return; }
var spans = document.getElementsByTagName("span");
// loop through all span tags
for (var i=0; i<spans.length; i++)
{
var spn = spans[i];
if (spn != null)
{
// if the span has a class with name "editText", then
// configure it
if (((' '+spn.className+' ').indexOf("editText") != -1) && (spn.id))
{
this._spanToEdit = spn;
$addHandler(spn, 'click', this._changeDisplayDelegate);
spn.style.cursor = "pointer";
spn.title = "Click to edit ...";
}
}
}
},
What this method does is parse the HTML of the Web Form,
collects all span HTML tags that have a class named "editText" and
then for each span it finds under that category, it adds a click event using
the $addHandler shorthand syntax provided by the AJAX client library. Using
that method requires you to specify the HTML control, the event name, and the
delegate that holds a reference to the function to be executed on the firing of
the event. Finally, the method adds a title and styles the cursor whenever it
hovers over the editable span.
When the user clicks on the editable span, the ChangeDisplay
function has to be fired. This function is displayed below.
Listing 4
// This method changes the display from a span tag
// into a textbox for the user to use to add the new
// text
ChangeDisplay: function()
{
spn = this.get_spanToEdit();
if (!this.get_editMode())
{
width = Sys.UI.DomElement.getBounds(spn).width + 20;
height = Sys.UI.DomElement.getBounds(spn).height + 2;
if(width < 100)
width = 150;
if(height < 40)
spn.innerHTML = "<input id=\"" + spn.id +
"_field\" style=\"width: " + width +
"px; height: " + height +
"px;\" maxlength=\"254\" type=\"text\" value=\"" +
spn.innerHTML +
"\" onkeypress=\"return FieldEnter(this,event,'" +
spn.id + "')\" onfocus=\"HighLight(this);\" " +
" onblur=\"NoHighLight(this);" +
" return FieldBlur(this,'" + spn.id + "');\" />";
else
spn.innerHTML = "<textarea name=\"textarea\" id=\"" + spn.id +
"_field\" style=\"width: " + width +
"%; height: " + height +
"px;\" onfocus=\"HighLight(this);\"" +
" onblur=\"NoHighLight(this); return FieldBlur(this,'"
+ spn.id + "');\">" + spn.innerHTML + "</textarea>";
this._editMode = true;
}
spn.firstChild.focus();
},
The function checks first to see if the script is in edit
mode or not. The width and height of the editable areas are calculated using
the AJAX Client Library DOM methods. Then based on the width of the editable
content, a Textbox or TextArea containing the span’s content is to be displayed
inside the span HTML. That is it. Now the editable span, instead of holding
directly the static text, it holds a Textbox that in turn contains the text to
be edited!
In case the editable text is placed in a Textbox, a set of
events should be handled, mainly the onkeypress, onfocus, and onblur.
When the text is edited inside the Textbox either an Enter
hit or just removing the cursor from over the Textbox would cause the text to
be updated in configured data store which might be an XML file or database
table.
When an Enter is hit while editing the text, the FieldEnter
function has to be executed and this function is shown in Listing 5.
Listing 5
// This method handles a keypress event inside the
// editable area and fires a WebRequest to the server
// to update the edited text in the data store
FieldEnter: function(ctrl, event, fieldID)
{
event = (event) ? event : window.event;
if (event.keyCode == 13 && ctrl.value != "")
{
// Execute a POST asynchornous request
this.UpdateDataStore($get(fieldID), ctrl);
// remove glow
NoHighLight($get(fieldID));
// Page is not in editing stage
this._editMode = false;
return false;
}
else {
return true;
}
},
This function takes as input the span ID together with the
event object. Then a check is done to determine if an Enter key was hit and
accordingly the UpdateDataStore is called. The UpdateDataSource function is
shown below.
Listing 6
// This method is responsible to contact the server
// and update the edited text in a data store
UpdateDataStore: function(elem, ctrl)
{
// Instantiate the WebRequest object.
var webRequest = new Sys.Net.WebRequest();
// Set the request Url.
webRequest.set_url(this.get_postPage());
// Set the request verb.
webRequest.set_httpVerb("POST");
// Encode the content before sending it to the server
var encodedContent = escape(ctrl.value);
encodedContent= encodedContent.replace(/\//g,"%2F");
encodedContent= encodedContent.replace(/\?/g,"%3F");
encodedContent= encodedContent.replace(/=/g,"%3D");
encodedContent= encodedContent.replace(/&/g,"%26");
encodedContent= encodedContent.replace(/@/g,"%40");
var body = "fieldName=" + escape(elem.id) + "&content=" + encodedContent;
if (this.get_otherVars() != "")
body = body + "&" + this.get_otherVars();
webRequest.set_body(body);
webRequest.get_headers()["Content-Length"] = body.length;
// Set the web request completed event handler,
// for processing return data.
// Make sure to add the createDelegate so the complete method will run
// under the same context of this class instance, means being able to
// accesss properties on this instance of the class
webRequest.add_completed(Function.createDelegate(this,
this.OnUpateStoreCompleted));
// Execute the request.
webRequest.invoke();
},
The above method creates a new WebRequest using the AJAX
Sys.Net.WebRequest. Then it sets the URL to post to, specifies the request is a
POST request, gets the edited text, encodes it in case it contains any HTML
tags, and sets the body of the request. Before invoking that request, the
request has been configured with a Completed function to execute once the
response is back. Finally, a call to invoke the request is done.
The OnUpdateStoreCompleted function executes when the
response is back from the server. The code for this function is shown below.
Listing 7
// This the handler for the Web request completed event
// that is used to display return data.
OnUpateStoreCompleted: function(executor, eventArgs)
{
if (executor.get_responseAvailable())
{
var result = executor.get_responseData();
$get(this.get_uiElement()).innerHTML = result;
}
else
{
if (executor.get_timedOut())
alert("Request Timed Out. Sorry for the inconvience");
else
if (executor.get_aborted())
alert("Request Aborted. sorry for the inconvience");
}
}
The method will check if there is a response, then it
retrieves the data sent back from the server, and updates the editable span on
the page with the received response data. If on the other hand there was any
problem processing the request, an alert popup will be shown to the user.
Finally, the AJAX class has to be registered.
Listing 8
// Register the class
bhaidar.CodeSuite.AJAX.InlineEdit.registerClass("bhaidar.CodeSuite.AJAX.InlineEdit");