An example of In Place Editing with Scriptaculous

My blog RSS feeds have been abuzz lately about Prototype.js and the Scriptaculous libraries, built on top of Prototype, so I just HAD to find a project to use this stuff, and I did. So in the spirit of those who have put some much time into these libraries, I thought I would share how I used them and some of the code to actually inplement it.


My project is a content management system for our latest upgrade to our external site. This application will allow us to migrate our existing static content into a system where we can control and track editing and ownership. Although this sounds like a good fit for Domino, we decided to use .NET 2.0 which will allow us alot more freedom with respect to accessing the content.

So for the sake of brevity I’ll skip the details and go straight to our use of the In Place Editing module from Scriptaculous. This feature provides the developer with a way to make any text, or textarea, field and make it editable just by clicking on the text. It also gives the developer the ability to use AJAX to update the backend when the user is done.

The script works by monitoring an HTML container (div, span, etc.) for the click event, then rewriting the DOM tree to replace the container with an INPUT object. When the user clicks a button or link to finish the transaction, the container is swapped back into place and the data sent to a Javascript handler function to do your evil bidding.

The default behavior of the In-Place-Editor simply takes the new field value, posts it to a url, and places the returned text into the HTML container. If you want the ability to handle any error handling then you need to start using some of the options supplied, and make a slight change to the source code. With respect to the change in source code, I am most likely missing something simple since the code changed looks to be an option that could be set as needed.

After you extract the Scriptaculous source code, open up the “controls.js” file in the “src” directory. On line 489 is an option called “evalScripts” that by default is set to false, but this needs to be set to true if we want to return the results of the AJAX call to a specified script for parsing and processing. Again, if I am missing something obvious, let me know because I could not set this value successfully as an option.

evalScripts: true

Once that is set, let’s look at the client-side code where we place a field value into a “span” tag and then tell the scripts to translate it into an in-place-editable field. For those of you who have not followed the .NET for Domino Developers series, the following is the source code of a .NET page with some Javascript mixed in. Some of you may recognize some of the syntax from any ASP development you might have done. For those who have never worked with any flavor of ASP, the <%# EVAL(”fieldId”) %> syntax is how you reference a field value of a recordset when you are looping through the recordset. Also note the use of the form reference function from Prototype $(’fieldID’).


<span id="editableRoleDesc_<%# Eval("roleID") %>"><%# Eval("roleDesc") %> </span>
<script type="text/javascript">
var completed = function(t){updateDivFromAjaxCall(t,$('editableRoleDesc_<%# Eval("roleID") %>'),'< %# Eval("roleDesc") %>');}

new Ajax.InPlaceEditor('editableRoleDesc_<%# Eval("roleID") %>',
          'CMAjaxAdmin.aspx?reqtype=5&tbl=www_roles&fld=roleDesc&recid=<%# Eval("roleID") %>&keyfld=roleID',
          {onComplete:completed});
</script>

In this case, I was using a GridView object and placing this code inside an ItemTemplate to define the column. So I create a span object with an ID that uses the unique ID of the record, and then the value of the “roleDesc” field for the innerHTML of the span.

Next we insert some javascript that first creates a variable called “completed” and sets its value equal to a function call that will be called when the AJAX call returns. The variable “t” is the AJAX response object that we will need later to extract the status and values. The second value is the span ID, and the last value is the original value of the span, in case we have a failure.

The second line of the javascript code is the scriptaculous code to translate our span into an in-place editable field. The parameters are:

'editableRoleDesc_<%# Eval("roleID") %>'
This is the ID of our span object

'CMAjaxAdmin.aspx?reqtype=5&tbl=www_roles&fld=roleDesc&recid=<%#Eval("roleID")%>&keyfld=roleID'

This is the URL of my AJAX page that handles many different functions for this app. The reqType parameter defines which CASE will be evaluated on the server. The other variables tell the server-side function which table to modify, which field, the field name of the table key, and the key value. Eventually I will need to hide these names since this kind of information is very valuable to malicious users.

{onComplete:completed}
This tells the script to refer to the “completed” variable when the AJAX call has been made and the results received.

That’s the meat of the operation. The server side part is pretty simple as we are just going to capture the querystring variables for use in our SQL statement, execute, and send back some XML for our javascript function.

Dim strTblId As String = Request.QueryString("tbl")
Dim strFldName As String = Request.QueryString("fld")
Dim strRecId As String = Request.QueryString("recid")
Dim strKeyFld As String = Request.QueryString("keyfld")
Dim strFldValue As String = Request.Form.Item("value")

Here is where we capture both the information from the querystring, and the single field that is posted from the In-Place-Edit form, which has a field called “value”. Then we create our SQL statement, and establish the connection.

' setup the connection and establish the insert statement
strSqlCommand = "UPDATE " + strTblId + " SET " + strFldName + "='" + strFldValue 
strSqlCommand += "' WHERE " + strKeyFld + "=" + strRecId

sqlCmd.CommandText = strSqlCommand
sqlCmd.CommandType = CommandType.Text
sqlCmd.Connection = conn

Lastly, if the UPDATE works, we will return our XML with a “status” node, and a “returnval” node.


    Dim nodeParent As XmlNode = xDoc.CreateNode(XmlNodeType.Element, "", "response", "")
    Dim nodePI As XmlProcessingInstruction = xDoc.CreateProcessingInstruction("xml", "version='1.0'")
     xDoc.AppendChild(nodePI)
    childNode = xDoc.CreateNode(XmlNodeType.Element, "", "status", "")
    attNode = xDoc.CreateAttribute("value")
    attNode.Value = "success"
    childNode.Attributes.Append(attNode)
    nodeParent.AppendChild(childNode)
    xDoc.AppendChild(nodeParent)
    childNode = xDoc.CreateNode(XmlNodeType.Element, "", "returnval", "")
    attNode = xDoc.CreateAttribute("value")
    attNode.Value = strFldValue
    childNode.Attributes.Append(attNode)
    nodeParent.AppendChild(childNode)
    xDoc.AppendChild(nodeParent)

Then we get back to the calling page, and the javascript that updates our span when we have a success.

function updateDivFromAjaxCall(originalRequest, obj, oldVal){
    if(originalRequest){
        var domXML = originalRequest.responseXML;
        var xmlDoc = domXML.documentElement;
    
        var statusTag = xmlDoc.getElementsByTagName("status")[0].attributes;
        var status = statusTag.getNamedItem("value").value;
        if (status == "success" ){
            var returnValTag = xmlDoc.getElementsByTagName("returnval")[0].attributes;
            var returnVal = returnValTag.getNamedItem("value").value;
            obj.innerHTML = returnVal;
        } else{
            var message = statusTag.getNamedItem("message").value;
            obj.innerHTML = oldVal;
            alert(message);
        }
    }
}

Here are the results:

4 Responses to “An example of In Place Editing with Scriptaculous”

  1. My Portal Project » Blog Archive » Scriptaculous Workflow designer Says:

    […] After being shown the Scriptaculous effects library, my usual first thought is “neat, but can I use this anywhere and not make it seem like gratuitous fluff?”. I found a good use for the in-place editing piece, and finally found a good fit for the sortable lists. […]

  2. CA Says:

    >>> strSqlCommand = “UPDATE ” + strTblId + ” SET ” + strFldName + “=’” + strFldValue

    Good lord man … sql injection! Please don’t consider using this technique on any public-facing site. In fact, you’ll get weird and mysterious breakage if the field so much as contains single quotes.

  3. Administrator Says:

    Very true CA. Yet this is only a sample, since many people have there own mehods and functions to check for sql injection.

  4. C Says:

    Hi,
    I have been searching for a way to use the inplace editor but without the need to submit it to another URL. I don’t suppose you know how this can be done at all?
    I have tried using an empty URL but this causes the ’saving…’ text to be displayed indefinitely :(

    I added the following line
    this.element.innerHTML = this.editField.value;

    to this section of code
    showSaving: function() {
    this.oldInnerHTML = this.element.innerHTML;
    this.element.innerHTML = this.options.savingText;
    Element.addClassName(this.element, this.options.savingClassName);
    this.element.style.backgroundColor = this.originalBackground;
    this.element.innerHTML = this.editField.value; //Added to remove the need for a URL to be used
    Element.show(this.element);
    }

    and it allows me to create a new place editor with an empty string for the URL but once editing has finished the span that was editable no longer is.

    Do you perhaps have any advice on this one based on what you have discovered while writing this page perhaps.

    Thanks for reading,

    C