Sie sind auf Seite 1von 24

Inline Editing

Sometimes it is nice to be able to edit a page without displaying form elements by default. For example, if you want to allow people who are logged in as administrators to edit sections of a page, you could make it so that the administrator could click on those sections to turn them into form elements. Our example below shows a simple table showing the presidents' first and last names:

The neat thing about this table is that the names become editable when clicked:

All the user has to do is type a new value and click on the edit icon to change the value in the table. An XMLHttpRequest is used to change the record in the database. The code is shown below.

Code Sample: MoreAjaxApps/Demos/Presidents.jsp


<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" import="java.sql.*"%> <html> <head> <title>Row to Form</title> <script type="text/javascript" src="../../prototype.js"></script> <script type="text/javascript" src="../../sarissa.js"></script> <script type="text/javascript" src="InlineEditing.js"></script> <link type="text/css" rel="stylesheet" href="Presidents.css"/> </head> <body> <form onsubmit="return false"> <% Connection conn = null;

PreparedStatement stmt = null; ResultSet rs = null; try { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); conn = DriverManager.getConnection("jdbc:odbc:Presidents"); String sql = "SELECT PresidentID, FirstName, LastName FROM Presidents"; stmt = conn.prepareStatement(sql); rs = stmt.executeQuery(); out.write("<h1>Presidents</h1>"); out.write("<table cellpadding='2' cellspacing='0'>"); while (rs.next()) { out.write("<tr id='" + rs.getString("PresidentID") + "'>"); out.write("<td onclick='Cell2Form(this)' title='FirstName'>" + rs.getString("FirstName") + "</td>"); out.write("<td onclick='Cell2Form(this)' title='LastName'>" + rs.getString("LastName") + "</td>"); out.write("</tr>"); } out.write("</table>"); } catch(Exception e) { out.write("failed: " + e.toString()); } finally { if (rs != null) rs.close(); if (stmt != null) stmt.close(); if (conn != null) conn.close(); } %> </form> </body> </html>

Code Explanation The code queries the database for the presidents' first and last names and then iterates through the resultset creating a table row for each president. Clicking a table cell will call the Cell2Form() function, passing in the table cell itself.

Code Sample: MoreAjaxApps/Demos/InlineEditing.js


function Cell2Form(CELL) { if (CELL.firstChild.nodeType == 1) { return true; } var strXML = "<td>" + CELL.innerHTML + "</td>"; var strXslPath = "Cell2Form.xsl";

new Ajax.Request(strXslPath, { method: "get", onComplete: _transform }); function _transform(REQ) { var strForm = Transform(strXML,REQ.responseXML); CELL.innerHTML=strForm; CELL.firstChild.firstChild.select(); }

function Transform(strXML,docXSL) { var parser = new DOMParser(); var xml = parser.parseFromString(strXML,"text/xml"); xml.async=false; var processor = new XSLTProcessor(); processor.importStylesheet(docXSL); var xmlDom = processor.transformToDocument(xml); var serializer = new XMLSerializer(); var result = serializer.serializeToString(xmlDom.documentElement); return result;

function SaveAndClose(CELL) { var cellName = CELL.title; var cellValue = CELL.firstChild.firstChild.value; var presID = CELL.parentNode.id; new Ajax.Request("SaveCell.jsp", { method: "get", parameters: "field=" + cellName + "&value=" + cellValue + "&pid=" + presID, onComplete: _result }); function _result(REQ) { if (REQ.responseText.indexOf("success") >= 0) { CELL.innerHTML = cellValue; Blink(CELL,1000,"Saved","Normal"); } else { alert(REQ.responseText); } } } function Blink(ELEM,TIME,ON,OFF,TIMEPAST)

var timePast = (typeof TIMEPAST != "undefined") ? TIMEPAST + 100 : 0; ELEM.className = (ELEM.className == ON) ? OFF : ON; if (timePast < TIME) { setTimeout(function () { Blink(ELEM,TIME,ON,OFF,timePast) },100); } else { ELEM.className = OFF; }

Code Explanation This is the JavaScript library. It has the following functions:

Cell2Form() 1. Creates a simple XML string containing the content of the clicked cell wrapped in <td></td> tags. 2. Uses prototype to load Cell2Form.xsl, which will be used to transform the XML string to an form control and then call the private _transform() function when the file is finished loading. _transform() Passes the XML string and the responseXML to the public Transform() function and assigns the value (a form control) to strForm. Writes strForm to the innerHTML property of the clicked cell. Now the cell contains the form control (and the edit image). Selects the text inside the form control so that the user can begin editing immediately. Transform() 1. Uses sarissa to transform the passed-in XML string against the passed in XSLT document. SaveAndClose() - This function is called when the edit button is clicked. 1. Uses prototype to send the name and value of the field being edited (e.g, FirstName or LastName) and the associated PresidentID. 2. The callback function (_result()) simply writes out the field name to the cell and calls the Blink() function. Blink() 1. Toggles the class name of the passed in element (ELEM) between the passed-in ON and OFF values for TIMEPAST milliseconds. This creates a blinking effect.

The SaveCell.jsp and Cell2Form.xsl files are shown below. The former updates the database. The latter transforms a data cell with text to a data cell with a form input element and image submit button.

Code Sample: MoreAjaxApps/Demos/SaveCell.jsp


<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" import="java.sql.*"%> <% Connection conn = null; PreparedStatement stmt = null; try { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); conn = DriverManager.getConnection("jdbc:odbc:Presidents"); String String String String = ?"; field = request.getParameter("field"); value = request.getParameter("value").replaceAll("'","''"); presID = request.getParameter("pid"); sql = "UPDATE Presidents SET " + field + " = ? WHERE PresidentID

stmt = conn.prepareStatement(sql); stmt.setString(1, value); stmt.setString(2, presID); stmt.executeUpdate(); out.write("success"); } catch (Exception e) { out.write("Failed:\n\n " + e.toString()); } finally { if (stmt != null) stmt.close(); if (conn != null) conn.close(); } %>

Code Sample: MoreAjaxApps/Demos/Cell2Form.xsl


<?xml version='1.0'?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <xsl:apply-templates select="td"/> </xsl:template> <xsl:template match="td"> <div> <input type="text" value="{.}"/> <input type="image" title="Save and Close" onclick="SaveAndClose(this.parentNode.parentNode)" src="Edit.gif" /> </div> </xsl:template> </xsl:stylesheet>

Exercise: Extending the Inline Editing Page


Duration: 20 to 30 minutes. In this exercise, you will extend the inline editing page to make it possible for users to edit the presidents biography as well as his first and last names.

This exercise requires knowledge of the server-side language used in this class (e.g, JSP, PHP, ASP, or ColdFusion, etc.) and XSLT. If you are not experienced with these technologies, you should pair up with someone who is.
1. Open MoreAjaxApps/Exercises/Presidents.jsp for editing.

Modify the code so that it displays the presidents' biographies as well. The database field is "Bio". 2. Open MoreAjaxApps/Exercises/Cell2Form.xsl for editing. o Modify the code so that it outputs an input text field if the content of the cell is shorter than 30 characters and a textarea if it is 30 characters or longer.
o

Where is the solution?

Detailed Information on Demand


Netflix, the online movie rental company, has a cool feature on their website (http://www.netflix.com) that allows users to hover over a picture of a movie to get more detailed information about that movie. This makes it possible to give users the detailed information they want without downloading detailed information about every movie shown on the page. A screenshot is shown below:

We've done something similar (though not as fancy) with our Presidents page:

The code is shown below.

Code Sample: MoreAjaxApps/Demos/TableRowsMoreInfo.jsp


<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" import="java.sql.*"%> <% Connection conn = null; PreparedStatement stmt = null; ResultSet rs = null; try { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); conn = DriverManager.getConnection("jdbc:odbc:Presidents"); String sql = "SELECT PresidentID, FirstName, LastName, StartYear, EndYear, ImagePath FROM Presidents"; stmt = conn.prepareStatement(sql); rs = stmt.executeQuery(); %> <html> <head> <title>Table Rows - More info</title> <link href="TableRowsMoreInfo.css" type="text/css" rel="stylesheet"/>

<script type="text/javascript" src="../../prototype.js"></script> <script type="text/javascript"> function GetBio(TR,ID) { var bioDiv=document.getElementById("Bio"); new Ajax.Request("Bio.jsp", { method: "get", parameters: "id=" + ID, onComplete: _showBio }) function _showBio(REQ) { bioDiv.innerHTML = REQ.responseText; } bioDiv.style.marginTop = (ID * 50) + "px"; //position element (hack) bioDiv.style.visibility="visible"; } function HideBio(TR) { var bioDiv=document.getElementById("Bio"); bioDiv.style.visibility="hidden"; bioDiv.innerHTML=""; } </script> </head> <body> <h1>First 10 Presidents</h1> <table id="Table" cellspacing="0"> <thead> <tr> <th style="width:120px">President</th> <th style="width:60px">Years</th> <th style="width:50px">Image</th> </tr> </thead> <tbody> <% while (rs.next()) { %> <tr valign="top"> <td><%=rs.getString("FirstName") + " " + rs.getString("LastName")%></td> <td><%=rs.getString("StartYear")%>-<%=rs.getString("EndYear")%></td> <td onMouseOver="GetBio(this,<%=rs.getString("PresidentID")%>)" onMouseOut="HideBio(this)" style="cursor:pointer"><img src="Images/< %=rs.getString("ImagePath")%>"/></td> </tr> <% } %>

</tbody> </table> <div id="Bio"></div> </body> </html> <% } catch(Exception e) { out.write("failed: " + e.toString()); } finally { if (rs != null) rs.close(); if (stmt != null) stmt.close(); if (conn != null) conn.close(); } %>

Code Sample: MoreAjaxApps/Demos/Bio.jsp


<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" import="java.sql.*"%> <% Connection conn = null; PreparedStatement stmt = null; ResultSet rs = null; try { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); conn = DriverManager.getConnection("jdbc:odbc:Presidents"); String sql = "SELECT Bio FROM Presidents WHERE PresidentID=?"; stmt = conn.prepareStatement(sql); stmt.setString(1, request.getParameter("id")); rs = stmt.executeQuery(); if (rs.next()) { out.write(rs.getString("Bio")); } else { out.write("No president selected"); } } catch (Exception e) { out.write(e.toString()); } finally { if (rs != null) rs.close(); if (stmt != null) stmt.close(); if (conn != null) conn.close();

} %>

Code Explanation This TableRowsMoreInfo.jsp file creates the presidents HTML table. The cell containing the image has a mouseover event that creates a call to the GetBio() function, which uses Prototype/Ajax to call Bio.jsp and get the biography of the associated president.

Autologout
If a user is idle for a certain amount of time, it is often a good idea to force a logout, especially if there might be sensitive data on the screen. This is normally handled on the server side; however, if we want to hide the data on the screen and alert the user that the session has ended, we'll need to handle the session on the client as well. One way to handle this is described below: 1. When the user logs in, create a JavaScript timer with the window.setTimeout() method. The timer will call a function that ends the session after n minutes. 2. Whenever there is user activity, the timer must be restarted. 3. When the user explicitly logs out (e.g, clicks on a logout button), the timer must be killed. To illustrate, we will be forcing the presidents to login to our inline editing application. The logins are all first initial - lastname (e.g, "gwashington") and the passwords are all the word

"password". First, let's look at the application. It starts with a simple login form:

When the user logs in, the editable table appears. We also provide a Logout button, so the user can log himself out.

When the session times out, the page goes blank and an alert appears informing the user that the session has timed out:

After the user clicks on the OK button, the login form reappears. Let's look at the code. There are quite a few files involved. The main page is shown below:

Code Sample: MoreAjaxApps/Demos/PresidentsLogin.html


<html> <head> <title>Authentication</title> <link href="Login.css" type="text/css" rel="stylesheet"/> <script type="text/javascript" src="../../prototype.js"></script> <script type="text/javascript" src="../../sarissa.js"></script> <script type="text/javascript" src="DOM.js"></script> <script type="text/javascript" src="InlineEditing2.js"></script>

<script type="text/javascript" src="Auth.js"></script> <script type="text/javascript" src="Presidents.js"></script> <script type="text/javascript"> var UsersName; function init() { CheckLogin(); } function Controller(UNIT) { switch (UNIT) { case "Table" : ShowPresidents(); break; case "Logout" : Logout(); break; case "Login" : Login(arguments[1],arguments[2]); //Username and Password break; default : LoginForm(); break; } } function StartApp() { var loggedInDiv = document.getElementById("LoggedInDiv"); var logOutDiv = document.getElementById("LogoutDiv"); loggedInDiv.innerHTML = "Logged in as " + UsersName; loggedInDiv.style.display="block"; logOutDiv.style.display="block"; Controller("Table"); StartAutoLogoutTimer(); AttachEvent(document.body,"click",StartAutoLogoutTimer); } function EndApp() { clearTimeout(AutoLogoutTimer); var loggedInDiv = document.getElementById("LoggedInDiv"); var logOutDiv = document.getElementById("LogoutDiv"); loggedInDiv.innerHTML = ""; loggedInDiv.style.display="none"; logOutDiv.style.display="none"; Controller("LoginForm"); DetachEvent(document.body,"click",StartAutoLogoutTimer); } window.onload = init; </script> <link type="text/css" rel="stylesheet" href="Presidents.css"/> </head> <body>

<div id="LogoutDiv" style="display:none; float:right;"> <form> <input type="button" onClick="Controller('Logout')" value="Logout"/> </form> </div> <div id="LoggedInDiv" style="display:none; float:right; marginright:20px;"></div> <form onsubmit="return false" id="OutputForm">One moment please...</form> </body> </html>

Code Explanation This page contains the basic HTML elements that will hold data returned from the server:
<body> <div id="LogoutDiv" style="display:none; float:right;"> <form> <input type="button" onclick="Controller('Logout')" value="Logout"/> </form> </div> <div id="LoggedInDiv" style="display:none; float:right; marginright:20px;"></div> <form onsubmit="return false" id="OutputForm">One moment please...</form> </body>

The "OutputForm" form will hold the login form or the table data, depending on whether or not the user is logged in. This page also controls the flow of the application: When the page loads, the init() function is called, which in turn calls the CheckLogin() function, which is in the Auth.js library. It looks like this:
1.
2. function CheckLogin() 3. { 4. new Ajax.Request("Controller.jsp", 5. { 6. method: "get", 7. parameters: "req=LoggedIn", 8. onComplete: _results 9. }); 10. 11. function _results(REQ) 12. { 13. if (REQ.responseText.indexOf("failed") == -1) 14. { 15. UsersName = REQ.responseText; 16. StartApp(); 17. } 18. else 19. { 20. Controller("LoginForm"); 21. } 22. } }

It makes an Ajax request to Controller.jsp, which handles the assignment of tasks based on the req parameter. In this case, it will redirect to Helpers/LoggedIn.jsp, which returns the user's name if he's logged in and the text "failed" if he is not. Based on the response, the callback function, _results(), calls StartApp() to start the applicationor calls the Controller() function, passing in "LoginForm". Let's first see what happens when the user is not logged in:
o The Controller() function handles task assignment on the client: o function Controller(UNIT) o { o switch (UNIT) o { o case "Table" : o ShowPresidents(); o break; o case "Logout" : o Logout(); o break; o case "Login" : o Login(arguments[1],arguments[2]); //Username and Password o break; o default : o LoginForm(); o break; o } }

In this case, it calls the LoginForm() function, which is in the Auth.js library. It uses an Ajax request to get the login form and then displays the form and sets the onsubmit callback function:
function LoginForm() { new Ajax.Request("Controller.jsp", { method: "get", parameters: "req=LoginForm", onComplete: _results }); function _results(REQ) { var loginForm = document.getElementById("OutputForm"); loginForm.innerHTML = REQ.responseText; loginForm.onsubmit = function() { Controller("Login",loginForm.Username.value,loginForm.Pa ssword.value); return false; } } }

When the user logs in, the Controller() function is called again. This time it is passed "Login" as the request and two additional values: the username and password. The Controller function in turn calls the Login() function, passing in the two additional arguments:
o o case "Login" : o Login(arguments[1],arguments[2]); //Username and Password break;

23.

The Login() function, which is in the Auth.js library, tries to log the user in with an Ajax call. If it fails, it reports the error to the user. If it succeeds, it starts the application by calling StartApp(). Now the user is logged in. The StartApp() function: o Shows the divs that hold the user's name and the logout button:
o o o

Calls Controller() and passes in "Table" to get the Presidents table. Calls StartAutoLogoutTimer() to start the count down to the autologout. o Attaches a click event to the body. Every user click will cause the autologout timer to be reset. 24. The StartAutoLogoutTimer() function (in the Auth.js library) is relatively straightforward:
25. var AutoLogoutTimer = null; 26. function StartAutoLogoutTimer() 27. { 28. var sessionTime = 15; 29. clearTimeout(AutoLogoutTimer); 30. AutoLogoutTimer = setTimeout("Logout(true)", sessionTime * 1000); //15 seconds }

The trick is that it restarts the timer every time it is called. When the timer does run out, it will call Logout() and pass true, indicating that this is an autologout rather than a user-initiated logout. 31. The Logout() function uses an Ajax call to a file that hides the page, ends the session and then calls the EndApp() function, which reinitiates the page. If the AUTO flag is set to true, then it also pops up an alert letting the user know he's been logged out. The code is shown below.
32. function Logout(AUTO) 33. { 34. document.getElementById("OutputForm").innerHTML = ""; 35. new Ajax.Request("Controller.jsp", 36. { 37. method: "get", 38. parameters: "req=Logout", 39. onComplete: _results 40. }); 41.

42. function _results(REQ) 43. { 44. EndApp(); 45. if (AUTO) 46. { 47. alert("You have been logged out due to inactivity."); 48. } 49. } }

We won't look at all the server-side code, but the controller is worth checking out.

Code Sample: MoreAjaxApps/Demos/Controller.jsp


<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%> <% try { String req; if (request.getParameter("req") != null) { req = request.getParameter("req"); } else { req = "LoginForm"; } if (req.equals("Table")) { response.sendRedirect("Helpers/PresidentsTable.jsp"); } else if (req.equals("Login")) { String un = request.getParameter("username"); String pw = request.getParameter("password"); response.sendRedirect("Helpers/Login.jsp?username=" + un + "&password=" + pw); } else if (req.equals("LoggedIn")) { response.sendRedirect("Helpers/LoggedIn.jsp"); } else if (req.equals("Logout")) { response.sendRedirect("Helpers/Logout.jsp"); } else //LoginForm { response.sendRedirect("Helpers/LoginForm.jsp"); } } catch (Exception e) {

} %>

out.write(e.toString());

Code Explanation As you can see, this page simply checks the value of the URL parameter, req, and dishes out the work to helper files. This makes the flow of the application easier to follow and maintain.

Autocompletion
Developers have become very excited about "autocomplete" dropdowns since Google Suggest beta came out. A screenshot is shown below.

Writing your own autocompletion scripts would be a difficult undertaking. And it's unlikely to be worth your while, as there are many such scripts available for free. The script.aculo.us library

makes creating autocompleting text fields very easy and our demo below makes use of it:

The code is shown below.

Code Sample: MoreAjaxApps/Demos/AutoComplete.html


<html> <head> <title>AutoComplete</title> <link rel="stylesheet" type="text/css" href="AutoComplete.css"> <script type="text/javascript" src="../../prototype.js"></script> <script type="text/javascript" src="../../scriptaculous_src/scriptaculous.js"></script> <script type="text/javascript"> function init() { new Ajax.Autocompleter("President","PresidentOptions","AutoComplete.jsp"); } window.onload = init; </script> </head> <body> <form> <input autocomplete="off" type="text" size="40" name="President" id="President" /> <div class="auto_complete" id="PresidentOptions" style="display: none;"></div> </form> </body> </html>

Code Sample: MoreAjaxApps/Demos/AutoComplete.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1" import="java.sql.*"%> <% Connection conn = null; PreparedStatement stmt = null; ResultSet rs = null; try { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); conn = DriverManager.getConnection("jdbc:odbc:Presidents"); String sName = "%" + request.getParameter("President") + "%"; String sql = "SELECT PresidentID, FirstName, LastName FROM Presidents WHERE FirstName LIKE ? OR LastName LIKE ?"; stmt = conn.prepareStatement(sql); stmt.setString(1, sName); stmt.setString(2, sName); rs = stmt.executeQuery(); out.write("<ul>"); while (rs.next()) { out.write("<li>"); out.write(rs.getString("FirstName") + " " + rs.getString("LastName")); out.write("</li>"); } out.write("</ul>"); } catch(Exception e) { out.write("failed:" + e.toString()); } finally { if (rs != null) rs.close(); if (stmt != null) stmt.close(); if (conn != null) conn.close(); } %>

Code Explanation The HTML file simply has an form input element and a div for outputting the dropdown options. We create the autocomplete with script.aculo.us's Ajax.Autocompleter() method, which requires three arguments: the name of the input field, the name of the output div, and the file to get the results from. The server-side script simply returns the results as an unordered list. We format this list with CSS, which is in the associated AutoComplete.css file.

More Ajax Applications Conclusion

In this lesson of the Ajax tutorial, you have learned to apply some additional Ajax techniques you have learned. To continue to learn Ajax go to the top of this page and click on the next lesson in this Ajax Tutorial's Table of Contents. Last updated on 2009-03-22 All material in this More Ajax Applications is copyright 2010 Webucator. The purpose of this website is to help you learn Ajax on your own and use of the

Das könnte Ihnen auch gefallen