
本动手实验室将指导您使用 DWR(Direct Web Remoting)开发基于 AJAX 的 Web 应用程序。本文档旨在帮助您尽快入门。
在本实验室中,您将了解:(1)循序渐进地构建基于 DWR 的 AJAX 应用程序(2)如何将 web 应用程序配置文件 web.xml 配置为对 DWR 适用的文件(3)如何使用和编写名为 dwr.xml 的 DWR 配置文件(4)如何使用一些 DWR 实用函数。
预计时间:90-120 分钟
本教程假设您对以下技术有所了解或具有编程经验:
开始之前,需要在您的计算机中安装以下软件。dwr.jar 文件已经包含在动手实验室的压缩文件中,您不必自己动手下载。
在本练习中,您将使用 NetBeans 构建和运行“Chat Demo”示例应用程序(dwr.examples.chat NetBeans 项目)。所需的全部代码和库文件已经给出为一个现成的 NetBeans 项目。
1. 打开 dwr.examples.chat NetBeans 项目。

图 1.10:打开 dwr.examples.chat NetBeans 项目


图 1.12:解决


3. 生成和运行项目

图 1.16:运行

图 1.17:使用 AJAX 运行数据验证
DWR 为远程方法提供了一个测试页面。您只需要将 /dwr 附加到该 URL 后面。
1. 在您的浏览器中,进入 http://localhost:8080/dwr.examples.chat/dwr。此页面是一个调试页面,您可以在其中和后端应用程序进行交互。
2. 单击 Chat。(如下图 1.13 所示)它将显示出所有由 DWR 远程化的方法。

图 1.13:调试页面
3. 现在您看到两个由 DWR 远程化的方法 —— addMessage("") 和 getMessage(),这意味着可以通过匹配 JavaScript 对象对这两个方法进行调用。
4. 单击 getMessages() 方法的 Execute 按钮。(如下图 1.14 所示)

图 1.14:调试页面,单击 getMessages() 方法的 Execute 按钮
5. 您应看到下图 1.15。下图显示了调用 JavaScript 对象 Chat 的 getMessages() 方法而以 JSON 形式返回的数据。(注意:我们将在本课程的后面介绍 JSON。)

图 1.15:返回的数据
6.将任意字符串,本例中为 3rd message 键入 addMessage() 调用的灰色区域并单击 Execute 按钮。(如下图 1.16 所示)

图 1.16:调试页面,单击 addMessage() 方法的 Execute 按钮
7. 您应看到下图 1.17。下图显示了调用 JavaScript 对象 Chat 的 addMessage() 方法而返回的数据

图 1.17:返回的数据
在本步骤,您将通过了解组成 dwr.examples.chat 示例应用程序的部分来了解 DWR 的底层架构。
1. 首先,您的 web 应用程序中必须有 dwr.jar 文件。dwr.examples.chat 应用程序中有这个文件。可以通过展开 dwr.examples.chat 项目节点并选择 Libraries 来查看该文件。您应该看到 dwr.jar 文件。(如下图 1.30 所示)
注意:dwr.jar 文件可从 DWR home site 下载。

图 1.30:dwr.jar
2. 必须将 DWR servlet 映射包含到 web 应用程序的 web.xml 中。每个 DWR 应用程序都必须包含它。调试参数可以被设为 false 或 absent。
|
<servlet> <servlet-name>dwr-invoker</servlet-name> <display-name>DWR Servlet</display-name> <servlet-class>uk.ltd.getahead.dwr.DWRServlet</servlet-class> <init-param> <param-name>debug</param-name> <param-value>true</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>dwr-invoker</servlet-name> <url-pattern>/dwr/*</url-pattern> </servlet-mapping> |
可以在 Web 页->WEB-INF 节点下看到 dwr.examples.chat 应用程序的 web.xml 文件。(如下图 1.33 所示)

图 1.33:web.xml 文件必须包含 DWR servlet 映射
3. 您还必须将名称为 dwr.xml 的 DWR 配置文件和 web.xml 文件存放到同一个目录下。在此文件中,您要指定哪些 Java 类及其方法进行“远程化”。可在 Web 页->WEB-INF 节点下查看 dwr.examples.chat 应用程序的 dwr.xml 文件。(如下图 1.34 所示)

图 1.34:dwr.xml 文件
在本例中,dwr.xml 表明 mypackage.Chat Java 类的所有公共方法都将被远程化为 Chat JavaScript 对象。现在,让我们来看看 mypackage.Chat Java 类以及如何从浏览器中调用该类的方法。
4. 右键单击 源包->mypackage 下的 Chat.java 将其在源代码编辑器窗口中打开。(如下图 1.35 所示)

图 1.35:mypackage.Chat.java

1. 在 源包->mypackage 下双击 Chat.java 将其在源编辑器中打开。
2. 修改 Chat.java 文件,如下所示。需要添加的新代码段突出显示为 蓝色粗体。
| /* * Chat.java * * Created on May 29, 2006, 8:03 AM * * To change this template, choose Tools | Template Manager * and open the template in the editor. */ package mypackage; import java.util.LinkedList; import java.util.List; public class Chat{ private static int numberOfMessages = 0; public List addMessage(String text){ if (text != null && text.trim().length() > 0){ messages.addFirst(new Message(text)); while (messages.size() > 10){ messages.removeLast(); } numberOfMessages++; } return messages; } public List getMessages(){ return messages; } public int getNumberOfMessages(){ return numberOfMessages; } private static LinkedList messages = new LinkedList(); } |
3. 右键单击 dwr.examples.chat 项目节点并选择 运行。我们想要在此处进行测试的目的是要确保添加的新方法是远程化的。
4. 在浏览器中,进入 http://localhost:8080/dwr.examples.chat/dwr。这是一个调试页面,从中可以和后端应用程序进行交互。
5. 单击 Chat。(如下图 1.x 所示) 将显示由 DWR 远程化的所有方法。应包含 getNumberOfMessages()。也可以单击该方法的 Execute 按钮。将返回数目。(如下图 1.40 所示)

图 1.40:检验添加的新方法被正确远程化
现在您可以确定新方法已经由 DWR 正确远程化。现在让我们更改客户端。
6. 双击 Web 页 下的 index.html 将其在源编辑器中打开。
7. 修改 index.html 文件,如下所示。需要添加的新代码段突出显示为
蓝色粗体。
| <!DOCTYPE HTML PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Chat-Demo</title> <!-- You have to include these two JavaScript files --> <script type='text/javascript' src='dwr/engine.js'></script> <script type='text/javascript' src='dwr/util.js'></script> <!-- This JavaScript file is generated specifically for your application --> <script type='text/javascript' src='dwr/interface/Chat.js'></script> <script type='text/javascript'> function sendMessage(){ var text = DWRUtil.getValue("text"); DWRUtil.setValue("text", ""); // Invoke addMessage(text) method of the Chat class on // the server. The gotMessages is a callback function. Chat.addMessage(text, gotMessages); } function checkMessages(){ Chat.getMessages(gotMessages); // This code will not work - you do not want to call the method // synchronously. var chatcount = Chat.getNumberOfMessages(); DWRUtil.setValue("chatcount", chatcount); } // Callback function function gotMessages(messages){ var chatlog = ""; for (var data in messages){ chatlog = "<div>" + messages[data].text + "</div>" + chatlog; } DWRUtil.setValue("chatlog", chatlog); setTimeout("checkMessages()", 1000); } </script> </head> <body onload="setTimeout('checkMessages()', 1000)"> <p>Messages:</p> <div id="chatlog" style="border: 1px solid black;"></div> <p>Number of messages:</p> <div id="chatcount" style="border: 1px solid black;" ></div> <p> Your Message: <input id="text" /> <input type="button" value="Send" onclick="sendMessage()" /> </p> </body> </html> |
8. 右键单击 dwr.examples.chat 项目节点并选择 运行。
您将看到以下结果。(下图 1.43 所示)

图 1.43:消息字段的数目并未更新
注意到,以上运行情况和您的预期并不一样。chatcount 没有显示出来。这一定是有原因的。想一想这段代码为什么不能运行。
9. 修改 index.html 文件,如下所示。需要添加的新代码段突出显示为 蓝色粗体。
| <!DOCTYPE HTML PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>Chat-Demo</title> <!-- You have to include these two JavaScript files --> <script type='text/javascript' src='dwr/engine.js'></script> <script type='text/javascript' src='dwr/util.js'></script> <!-- This JavaScript file is generated specifically for your application --> <script type='text/javascript' src='dwr/interface/Chat.js'></script> <script type='text/javascript'> function sendMessage(){ var text = DWRUtil.getValue("text"); DWRUtil.setValue("text", ""); // Invoke addMessage(text) method of the Chat class on // the server. The gotMessages is a callback function. Chat.addMessage(text, gotMessages); } function checkMessages(){ Chat.getMessages(gotMessages); } function checkChatCount(){ // The getNumberOfMessages() needs to be called in asynchronous fashion Chat.getNumberOfMessages(gotChatCount); } // Callback function function gotMessages(messages){ var chatlog = ""; for (var data in messages){ chatlog = "<div>" + messages[data].text + "</div>" + chatlog; } DWRUtil.setValue("chatlog", chatlog); setTimeout("checkMessages()", 1000); // The checkChatCount() function gets called in 1000 ms. setTimeout("checkChatCount()", 1000); } // Callback function of the Chat.getNumberOfMessages(gotChatCount) function gotChatCount(ChatCount){ DWRUtil.setValue("chatcount", ChatCount); } </script> </head> <body onload="setTimeout('checkMessages()', 1000)"> <p>Messages:</p> <div id="chatlog" style="border: 1px solid black;"></div> <p>Number of messages:</p> <div id="chatcount" style="border: 1px solid black;" ></div> <p> Your Message: <input id="text" /> <input type="button" value="Send" onclick="sendMessage()" /> </p> </body> </html> |
10. 右键单击 dwr.examples.chat 项目节点并选择 清理并生成项目,部署项目,然后以连续顺序 运行。您应看到以下结果(如下图 1.45 所示)

图 1.45:现在消息数被正确显示出来
| public String
getLastMessage(){ if (messages.size()== 0) { return ""; } //getFirst will actually return the "last" message based on this data //structure Message m = (Message)messages.getFirst(); return (String)m.getText(); } |
在本练习中,您构建并运行了一个简单的 DWR 示例应用程序。了解了 DWR 应用程序是如何构造的。也添加了另一个用于远程化的 Java 方法。
在本练习中,您将构建和运行另一个 DWR 示例应用程序,名称为“Dynamic Form Editing”。此处您将练习如何将一个名称为 Person 的 JavaBean 对象作为参数传给一个远程化方法。(2.1)所需的所有代码和库文件都在一个现成的 NetBeans 项目中。在步骤(2.2)中,将按照本文档中的说明来更改代码。步骤(2.3)留做您自己的练习。
1. 打开 dwr.examples.form-editing NetBeans 项目
3. 生成和运行项目。

图 2.10:运行“Dynamic Form Editing”示例 DWR 应用程序的结果

图 2.11:编写表单
在本步骤中,将把 Person 类的 name 字段由当前的 String 类型修改为 JavaBean 类。Person 类被认为是一个嵌套类,因为它包含另一个类。
1. 双击 dwr.examples.form-editing->源包->uk.ltd.getahead.testdwr 下的 Person.java 将其在 NetBeans 的源编辑器中打开。
2. 修改 Person.java,如下所示 —— 将 name 字段的 String 类型改为 Name 类。需要更改的代码段显示为 蓝色粗 体。在 Person.java 文件中, 有四处需要进行从 String 到 Name 的更改。
| package uk.ltd.getahead.testdwr; public class Person { private Name name; private String address; private int id; private float salary; // No arg constructor to make it a JavaBean public Person(){ } public Person(Name name, String address, int id, float salary){ this.name = name; this.address = address; this.salary = salary; this.id = id; } public Name getName() { return name; } public void setName(Name name) { this.name = name; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public float getSalary() { return salary; } public void setSalary(float salary) { this.salary = salary; } public int getId() { return id; } public void setId(int id) { this.id = id; } } |
3. 现在要创建 Name 类。右键单击 dwr.examples.form-editing->源包 下的 uk.ltd.getahead.testdwr 包节点并选择 新建->Java 类。(如下图 2.21 所示)

图 2.21:创建一个新的 Java 类
4. 在 名称和位置 窗格下的 类名: 字段处键入 Name。单击 完成。(如下图 2.22 所示)

图 2.22:Name 类
5. 修改由 IDE 生成的 Name.java 代码,如下所示。(如代码 2.23 所示)需要添加的代码段突出显示为 蓝色粗 体。请确保没有带参数的构造方法。否则,DWR 将在类转换中出现问题。
| package uk.ltd.getahead.testdwr; /** * * @author sang */ public class Name { private String firstname; private String middlename; private String lastname; // No arg constructor public Name() { } public Name(String firstname, String middlename, String lastname) { this.firstname = firstname; this.middlename = middlename; this.lastname = lastname; } public String getFirstname() { return firstname; } public void setFirstname(String firstname) { this.firstname = firstname; } public String getMiddlename() { return middlename; } public void setMiddlename(String middlename) { this.middlename = middlename; } public String getLastname() { return lastname; } public void setLastname(String lastname) { this.lastname = lastname; } } |
| private void
createPeople() { Person fred = new Person(new Name("Fred", "Middle", "Shin"), "1 Red Street", 2, 100000.0f); Person jim = new Person(new Name("Jim", "M", "Clinton"), "42 Brown Lane", 3, 20000.0f); Person shiela = new Person(new Name("Shiela", "Mi", "Hutcherson"), "12 Yellow Road", 4, 3000000.0f); people.put(new Integer(fred.getId()), fred); people.put(new Integer(jim.getId()), jim); people.put(new Integer(shiela.getId()), shiela); } private static final String SESSION_CLICKS = "sessionClicks"; private static final String CONTEXT_CLICKS = "contextClicks"; private static int nextId = 10; private final Map people = new HashMap(); private Person person = new Person(new Name("John Doe", "Mid", "Thompson"), new Date().toString(), 1, 100000.0F); } |
| <!DOCTYPE HTML PUBLIC
"-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>DWR Examples from DWR website</title> <script type='text/javascript' src='dwr/engine.js'></script> <script type='text/javascript' src='dwr/interface/Demo.js'></script> <script type='text/javascript' src='dwr/util.js'></script> <script type='text/javascript'> // This function gets called when window is loaded. // The fillForm is a callback function to be called asynchronously. function readPerson() { Demo.getExamplePerson(fillForm); } function writePerson() { DWRUtil.getValues(person); DWRUtil.getValues(person.name); DWRUtil.getValues(person.address); Demo.setExamplePerson(null, person); } function clearPerson() { //person = { id:"", name:"", address:"", salary:"" }; person = { id:"", firstname:"", middlename:"", lastname:"", address:"", salary:"" }; DWRUtil.setValues(person); } var person; var name; // Callback function function fillForm(aperson) { // Add some alert messages to display the data that is received from the server alert(DWRUtil.toDescriptiveString(aperson)); alert(DWRUtil.toDescriptiveString(aperson.name)); person = aperson; DWRUtil.setValues(person); // Note the easy syntax for accessing the Name child object from Person parent object name = aperson.name; DWRUtil.setValues(name); } // Initialization that is called when window is loaded function init() { DWRUtil.useLoadingMessage(); readPerson(); } // Call init function when window is loaded window.onload = init; </script> </head> <body> <h1>Dynamic Form Editing</h1> <p>This is a very simple demonstration of form editing using DWR.</a> <h2>Demo</h2> <table> <tr> <td>First Name:</td> <td><input id="firstname" type="text"/></td> <td>Middle Name:</td> <td><input id="middlename" type="text"/></td> <td>Last Name:</td> <td><input id="lastname" type="text"/></td> </tr> <tr> <td>Salary:</td> <td><input id="salary" type="text"/></td> </tr> <tr> <td>ID:</td> <td><input id="id" type="text"/></td> </tr> <tr> <td>Address:</td> <td><input type="text" id="address"/></td> </tr> <tr> <td colspan="2" align="right"> <input type="button" value="Clear" onclick="clearPerson()"/> <input type="button" value="Read" onclick="readPerson()"/> <input type="button" value="Write" onclick="writePerson()"/> </td> </tr> </table> </body> </html> |
| <?xml version="1.0"
encoding="UTF-8"?> <!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN" "http://www.getahead.ltd.uk/dwr/dwr10.dtd"> <dwr> <allow> <create creator="new" javascript="Demo" scope="session"> <param name="class" value="uk.ltd.getahead.testdwr.Demo"/> </create> <create creator="new" javascript="Chat"> <param name="class" value="uk.ltd.getahead.testdwr.Chat"/> </create> <create creator="new" javascript="UrlValidator"> <param name="class" value="org.apache.commons.validator.UrlValidator"/> </create> <create creator="new" javascript="CreditCardValidator"> <param name="class" value="org.apache.commons.validator.CreditCardValidator"/> </create> <create creator="script" javascript="EmailValidator" scope="application"> <param name="language" value="beanshell"/> <param name="script"> import org.apache.commons.validator.EmailValidator; return EmailValidator.getInstance(); </param> </create> <create creator="new" javascript="XOM"> <param name="class" value="uk.ltd.getahead.testdwr.XOMDemo"/> </create> <create creator="new" javascript="JDOM"> <param name="class" value="uk.ltd.getahead.testdwr.JDOMDemo"/> </create> <create creator="new" javascript="DOM4J"> <param name="class" value="uk.ltd.getahead.testdwr.DOM4JDemo"/> </create> <create creator="new" javascript="DOM"> <param name="class" value="uk.ltd.getahead.testdwr.DOMDemo"/> </create> <convert converter="bean" match="uk.ltd.getahead.testdwr.Person"/> <convert converter="bean" match="uk.ltd.getahead.testdwr.Message"/> <convert converter="bean" match="uk.ltd.getahead.testdwr.Name"/> <convert converter="bean" match="$Proxy*"/> </allow> </dwr> |
7. 右键单击 dwr.examples.form-editing 项目并选择 运行。您应看到一条显示出的内容为返回数据的 alert 消息。(如下图 2.27 所示)

图 2.27:回调函数中第一条 alert 消息
8. 单击 alert 消息的 OK。您会看到第二条 alert 消息,其中显示 Name 对象的内容。
9. 单击第二条 alert 消息的 OK。(如下图 2.28 所示)

图 2.28:回调函数中的第二条 alert 消息


作为练习,将一个 JavaBean 类作为 Address 字段(代替 String 类型)。按照以下代码 2.30 编写 Address.java。如以上 步骤(2.2),对 Demo.java、index.html 和 dwr.xml 文件作出修改。重新生成并测试该应用程序。
| package uk.ltd.getahead.testdwr; /** * * @author sang */ public class Address{ private String street; private String state; private String zipcode; /** Creates a new instance of Address*/ public Address() { } public Address(String street, String state, String zipcode) { this.street = street; this.state = state; this.zipcode = zipcode; } public String getstreet() { return street; } public void setstreet(String street) { this.street = street; } public String getstate() { return state; } public void setstate(String state) { this.state = state; } public String getzipcode() { return zipcode; } public void setzipcode(String zipcode) { this.zipcode = zipcode; } } |
在本练习中,您了解了如何将 Java 对象作为远程方法的参数传递。也了解了如何将嵌套 Java 对象作为参数传递。
返回顶部
在本练习中,您将构建并运行另一个名称为“Dynamic Table Editing”的 DWR 示例应用程序(dwr.examples.table-editing project)。您将了解如何使用 DWRUtil.addRows() 和 DWRUtil.removeAllRows() 实用函数来操纵表格。
在进行此练习前,请通过阅读 DWR 网站的 Generating Tables tutorial 来熟悉这两个实用函数。步骤(3.1)中所需的所有代码和库文件已经提供在一个现成的 NetBeans 项目中。


图 3.11:编辑 Shiela 的地址

图 3.12:再添加一行
| package uk.ltd.getahead.testdwr; public class Person { /* Getters and setters go here */ private String name; private String address; private int id; private float salary; private String hobby; // No arg constructor to make it a JavaBean public Person(){ } public Person(String name, String address, int id, float salary, String hobby){ this.name = name; this.address = address; this.salary = salary; this.id = id; this.hobby = hobby; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public float getSalary() { return salary; } public void setSalary(float salary) { this.salary = salary; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getHobby() { return hobby; } public void setHobby(String hobby) { this.hobby= hobby; } } |
| /** * Setup the list of people. */ private void createPeople() { Person fred = new Person("Fred", "1 Red Street", 2, 100000.0f, "Swimming"); Person jim = new Person("Jim", "42 Brown Lane", 3, 20000.0f, "Golfing"); Person shiela = new Person("Shiela", "12 Yellow Road", 4, 3000000.0f, "Dancing"); people.put(new Integer(fred.getId()), fred); people.put(new Integer(jim.getId()), jim); people.put(new Integer(shiela.getId()), shiela); } private static final String SESSION_CLICKS = "sessionClicks"; private static final String CONTEXT_CLICKS = "contextClicks"; private static int nextId = 10; private final Map people = new HashMap(); private Person person = new Person("John Doe", new Date().toString(), 1, 100000.0F, "Walking"); |
| <!DOCTYPE HTML PUBLIC
"-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>DWR Examples from DWR website</title> <script type='text/javascript' src='dwr/engine.js'></script> <script type='text/javascript' src='dwr/interface/Demo.js'></script> <script type='text/javascript' src='dwr/util.js'></script> <script type='text/javascript'> // Callback function for Delete method function update() { Demo.getAllPeople(fillTable); } // Functions to be passed to DWRUtil.addRows var getName = function(person) { return person.name }; var getDoB = function(person) { return person.address }; // if we return to using dates, add .toLocaleDateString() var getSalary = function(person) { return person.salary }; var getHobby = function(person) { return person.hobby}; var getEdit = function(person) { return '<input type="button" value="Edit" onclick="readPerson('+person.id+')"/>'; }; var getDelete = function(person) { return '<input type="button" value="Delete" onclick="deletePerson('+person.id+', \''+person.name+'\')"/>'; }; // Callback function for getAllPeople method // The table is reconstructed function fillTable(people) { DWRUtil.removeAllRows("peoplebody"); DWRUtil.addRows("peoplebody", people, [ getName, getDoB, getSalary, getHobby, getEdit, getDelete ]) } // Event handler to be called when Edit button is clicked function readPerson(id) { Demo.getPerson(fillForm, id); } // Event handler to be called when Delete button is clicked function deletePerson(personid, name) { if (confirm("Are you sure you want to delete " + name + "?")) { Demo.deletePerson(update, { id:personid }); } } // Event handler to be called when Save button is clicked function writePerson() { DWRUtil.getValues(person); Demo.addPerson(update, person); } var person = { id:-1, name:null, address:null, salary:null, hobby:null }; // Event handler to be called when Clear button is clicked function clearPerson() { person = { id:-1, name:null, address:null, salary:null, hobby:null }; DWRUtil.setValues(person); } // Callback function for readPerson method function fillForm(aperson) { person = aperson; DWRUtil.setValues(person); } // Function that will be called when page is loaded function init() { DWRUtil.useLoadingMessage(); update(); } // Set init function to be called when page is loaded window.onload = init; </script> </head> <body> <h1>Dynamically Editing a Table</h1> <p>This demo stores the list of people in your session, so the editing relies on <b>Cookies</b>. DWR can use application, session and request scope to store beans</p> <h2>Demo</h2> <table border="1" class="rowed"> <thead> <tr> <th>Name</th> <th>Address</th> <th>Salary</th> <th>Hobby</th> <th colspan="2">Actions</th> </tr> </thead> <tbody id="peoplebody"> </tbody> </table> <h4>Edit Person</h4> <table> <tr> <td>ID:</td> <td><span id="id">-1</span></td> </tr> <tr> <td>Name:</td> <td><input id="name" type="text"/></td> </tr> <tr> <td>Salary:</td> <td><input id="salary" type="text"/></td> </tr> <tr> <td>Address:</td> <td><input type="text" id="address"/></td> </tr> <tr> <td>Hobby:</td> <td><input type="text" id="hobby"/></td> </tr> <tr> <td colspan="2" align="right"> <input type="button" value="Save" onclick="writePerson()"/> <input type="button" value="Clear" onclick="clearPerson()"/> </td> </tr> </table> </body> </html> |

在本练习中,您将构建并运行另一个 DWR 示例应用程序,名称为“Dynamically Populating a Selection List”。

