+ -
当前位置:首页 → 问答吧 → [原创] 用 PHPRPC 实现 Ajax 级联下拉菜单

[原创] 用 PHPRPC 实现 Ajax 级联下拉菜单

时间:2006-06-07

来源:互联网

该文首发于:CoolCode.CN

级联下拉菜单就是从一个下拉菜单中选中一项后,相应的另一个下拉菜单的内容会随之改变。

一般来说,最简单的,就是每次选中都提交一次表单,刷新整个页面。这也是用户体验度最差的。

另一种是把所有选项在第一次加载时就全部载入整个页面中的 JavaScript 数组中,然后级联通过 JavaScript 来控制,在整个数据量不大时,这是一个不错的实现无刷新并且快速的方法,但是当整个数据量非常大时,这种方法就会使第一次加载变得非常慢了。

还有就是采用 Ajax 方式,即开始只载入第一层菜单的内容,当用户选中第一层菜单的某项时,再通过 XmlHttpRequest 来获取相应选项所对应的第二层菜单的内容。这种方式效果最好,但是采用传统方式来编写这样的 Ajax 程序代码量会比较多。而且如果设计不好,服务器端返回菜单内容的程序的可复用性也会很差。

但是在本文中你会看到用 PHPRPC 来实现这种 Ajax 效果是多么的简单,并且还会具有非常高的可复用性。

本文以省市两级级联下拉菜单为例,为了举例方便,本文中采用的是 SQLite 数据库,因为这个文件型数据库比较容易部署,而且查询效率很高(当然创建该数据库的效率不高,但创建仅一次而已,该数据库在该程序中内容是不变的),不过服务器需要安装 SQLite 扩展。

这个数据库中的表只有 2 个,一个 province 表,一个 city 表。province 表中,只有 id 和 name 两个字段,分别是省份编号(主键)和省名。city 表中,有 id、name 和 pid 三个字段,id 是城市编号,name 是城市名,pid 是城市所在省的编号,与 province 表中的省份编号相对应。

创建该数据库的程序这里就不给出来了,它包含在后面提供的实例下载中。

下面来看看创建这个程序的服务器端有多么简单,为了提高可复用性,我们把服务器端分为了 2 个文件,一个是 function.php,另一个是 rpc.php。function.php 中定义了实际的远程调用函数,但是他们也可以作为服务器端的本地函数调用,你会发现他们跟服务器端的普通函数没有任何区别:
复制PHP内容到剪贴板
PHP代码:

<?php
$sqlite = new SQLiteDatabase('area.db');

function get_province() {
    global $sqlite;
    $sql = "select * from province order by id";
    return $sqlite->arrayQuery($sql, SQLITE_ASSOC);
}

function get_city($pid) {
    global $sqlite;
    $pid = sqlite_escape_string($pid);
    $sql = "select * from city where pid = $pid order by id";
    return $sqlite->arrayQuery($sql, SQLITE_ASSOC);
}
?>

而 rpc.php 更加简单,它是提供给客户端调用的接口,它只有 3 行语句:
复制PHP内容到剪贴板
PHP代码:

<?php
require_once('function.php');
require_once('phprpc_server.php');
new phprpc_server(array('get_province', 'get_city'));
?>

其中最后一句,就是指定哪些函数要暴露给客户端。只有指定的函数客户端才可以调用,这样可以保证服务器的安全性。

服务器端到此就创建完了。你会发现服务器端只负责把数据查询出来返回给客户端就完事了,其它的不做任何处理。

那么下面该看一看客户端了,客户端虽然很简单,但是我还是把它分成了两个文件,一个 js 文件,一个 html 文件,你会发现用 PHPRPC,客户端都不需要用 PHP。
复制内容到剪贴板
代码:
// 创建 phprpc 客户端对象 rpc
phprpc_client.create('rpc');

var city = []; // 用于缓存已加载的城市数据

/*
* 清除 select 中的选项,该方法可复用
*
* so: 要清除选项的 select 对象
*
*/
function clear_select(so) {
    for (var i = so.options.length - 1; i > -1; i--) {
        // 有些浏览器不支持 options 属性的 remove 方法,
        // 但支持 DOM 的 removeChild 方法(比如:Konqueror)
        if (so.options.remove) {
            so.options.remove(i);
        }
        else {
            so.removeChild(so.options[i]);
        }
    }
}

/*
* 设置 select 中的选项,该方法可复用
*
* so: 要设置选项的 select 对象
*  d: 选项数据数组
* vf: 选项值所对应的数组中的字段名
* tf: 选项文本所对应的数组中的字段名
*/
function set_select(so, d, vf, tf) {
    for (var i = 0, n = d.length; i < n; i++) {
        var opt = document.createElement('option');
        opt.text = d[i][tf];
        opt.value = d[i][vf];
        // 有些浏览器不支持 options 属性的 add 方法,
        // 但支持 DOM 的 appendChild 方法(比如:Konqueror)
        if (so.options.add) {
            so.options.add(opt);
        }
        else {
            so.appendChild(opt);
        }
    }
}

// 设置省份的下拉菜单
function set_province_select(d) {
    var so = document.getElementById('province');
    set_select(so, d, 'id', 'name');
    // 设置首选省份的城市下拉列表
    change_province(1);
}

// 设置城市的下拉菜单
function set_city_select(d, vf, tf) {
    var so = document.getElementById('city');
    // 清空原有选项
    clear_select(so);
    // 设置新选项
    set_select(so, d, vf, tf);
}

// 当省份改变,相应的改变城市列表
function change_province(pid) {
    // 如果已缓存,则直接显示缓存中的列表
    if (city[pid]) {
        set_city_select(city[pid], 'id', 'name');
    }
    else {
        // 否则,先显示载入中
        set_city_select([['', 'Loading...']], 0, 1);
        // 然后调用远程过程载入城市信息
        // 调用远程过程时,最后一个参数指定的是回调函数
        rpc.get_city(pid, function (result) {
            // 把载入的数据放入缓存
            city[pid] = result;
            // 更新城市列表
            set_city_select(result, 'id', 'name');
        });
    }
}

// 定义当 rpc 客户端初始化(use_service)完毕后执行的内容
rpc.onready = function () {
    // 调用获取省份内容的远程过程,并设置回调函数为 set_province_select
    rpc.get_province(set_province_select);
}
复制内容到剪贴板
代码:
<html>
<head>
<script type="text/javascript" src="phprpc_client.js"></script>
<script type="text/javascript" src="area.js"></script>
</head>
<body onload="rpc.use_service('rpc.php');">
<select id="province" onchange="change_province(this.value);"></select>
<select id="city"></select>
</body>
</html>
上面的 html 中包含的 phprpc_client.js 是压缩版本(因为不需要用到加密,这里是 lite 压缩版)的,这样可以免去包含多个 js 文件的麻烦。

大家会发现这个程序不但可复用性好(比如 clear_select 和 set_select 两个函数也可以在其它程序中使用),而且使得整个程序的思路清晰,比如那个缓存功能,在这里实现的就非常的简单,而且效果也非常的好。


  • 演示程序
  • 实例下载


通过 PHPRPC,你不需要再专注于服务器端和客户端的数据格式交换,不需要再去考虑 XmlHttpRequest 对象的创建和使用,PHPRPC 会自动帮你完成这一切,你只需要关注具体的事务就可以了。用 PHPRPC 来做 Ajax 编程,就是这么简单。

作者: andot   发布时间: 2006-06-07

不错,支持一下

作者: cysin   发布时间: 2006-06-07

发布过楼主的作品:
http://www.phpchina.cn/bbs/viewt ... &extra=page%3D1

作者: dashan   发布时间: 2006-06-14

原来版主早就发布过了啊!多谢支持啊!

今天,PHPRPC 新版本 20060614 发布了!该版本增加了 ASP 版本的 PHPRPC 服务器和客户端的实现和最新的手册。其它地方也作了一些改进。相对于前几次更新,这次更新还是比较大的。

作者: andot   发布时间: 2006-06-14

引用:
原帖由 andot 于 2006-6-14 18:08 发表
原来版主早就发布过了啊!多谢支持啊!

今天,PHPRPC 新版本 20060614 发布了!该版本增加了 ASP 版本的 PHPRPC 服务器和客户端的实现和最新的手册。其它地方也作了一些改进。相对于前几次更新,这次更新还是比 ...
ASP?如果我只需要PHP还用升级么?

作者: cysin   发布时间: 2006-06-19

js 客户端也作了升级,现在可以处理 4 字节的 UTF8 字符串(虽然我还没有遇到过),可以支持 Mobile IE 上的加密传输等功能。PHP 的服务器当只有一个函数需要公开给客户端时,直接用字符串参数就行了,不再需要用一个元素的数组。如果 PHP 服务器上安装了 big_int 扩展,会调用该扩展加快速度。

其实升级的话,也不需要修改原来的程序。就用新的把旧的覆盖一下就行了。现在的版本如果没有特殊的需求的话(比如提供 Server-Push 的能力),应该不会再有改变了。

下个版本应该会是增加 .NET 实现的服务器和客户端的版本。

作者: andot   发布时间: 2006-06-20

非常棒的应用!

作者: Zero   发布时间: 2006-06-30

�l把�侵鞯拇��a�D�N一下啦.

�k公室的���X不支持下�d.拜托了.

作者: betterp   发布时间: 2006-07-05

经过半个月的开发与严格测试,PHPRPC_2.1-20060707 版终于发布了!

该版本修正了 xxtea.php 在 Linux 上的问题,该问题是由于在 Linux 上整数溢出处理方式不统一造成的。它使得 PHPRPC 的加密传输功能在某些 Linux 版本上不能正常工作。该问题目前已修正。

修正了 ASP 版本的 PHPRPC 服务器中返回的错误号格式不正确的问题,该问题会造成发生某些错误时不能被发现。该问题目前已修正。

这次更新的重点是增加了 PHPRPC for ASP.NET 服务器端。它功能强大且简单易用,它实现了 PHPRPC for PHP 服务器端的所有特征,包括引用参数传递、控制台输出重定向等功能。另外,它还增加了对重载方法的支持。 该 ASP.NET 版本以源代码形式提供,编译好的库文件 PHPRPC.dll 可以在 sample 中找到,但这个是用 .NET Framework SDK 2.0 编译的,如果需要在 1.0、1.1 或 Mono 上运行,需要自己重新编译。

该版本中还增加了 2 个 PHPRPC for ASP.NET 的例子。一个是简单的示例,另一个是通过 PHPRPC for ASP.NET 实现的安全的 Web Telnet 客户端,它充分显示了 PHPRPC for ASP.NET 的强大功能,这是我见过的唯一一个不需要客户端安装任何插件就可以使用的 Web Telnet 客户端,而且还带加密传输功能。尽管它目前对控制字符的显示支持还不够,但这个问题可能在以后会进行改进。

另外,该版本还增加了 .NET 版本的 keygen,可以用它来生成 keypair.php、keypair.js 和 KeyPair.cs,.NET 版本的 keygen 执行速度远远快于 PHP 版本。

同时,PHPRPC 用户指南也增加了 PHPRPC for ASP.NET 的内容。

下载:phprpc_2.1.zip
SourceForge.net 下载:下载页面

作者: andot   发布时间: 2006-07-07

看起来不错
准备学习一下用到我们的下一个项目中^^

作者: cysin   发布时间: 2006-07-07

PHPRPC 技术交流群:4927045

作者: andot   发布时间: 2006-07-08

phprpc_2.1-20060810.zip 版本中问题多多, 连 sample 都 run 出来有错。
文档资料太少,也太简单。不过这个PHPRPC 是个好东东, 希望大家一起来研究使用。

作者: dennis   发布时间: 2006-08-22

phprpc_2.1-20060810.zip 可以算是个仓促发布的版本。确实问题多多,而且没有文档。sample 不能运行的问题是目录结构改变了,但 sample 还是旧的,所以不能运行了。之所以这样是因为,考虑到目前还没有太多的人把 PHPRPC 投入到实际生产环境中去,而目前 PHPRPC 现在正在开发 3.0 版本,到时候连 API 接口都会有变化。所以,2.1 的开发打算就此停止了。所以,即使大家打算使用 PHPRPC,也不要用 2.1 了,等候 3.0 的发布吧。

作者: andot   发布时间: 2006-08-23

引用:
原帖由 andot 于 2006-8-23 16:21 发表
phprpc_2.1-20060810.zip 可以算是个仓促发布的版本。确实问题多多,而且没有文档。之所以这样是因为,考虑到目前还没有太多的人把 PHPRPC 投入到实际生产环境中去,而目前 PHPRPC 现在正在开发 3.0 版本,到时候连 API 接口都会有变化。所以,2.1 的开发打算就此停止了。所以,即使大家打算使用 PHPRPC,也不要用 2.1 了,等候 3.0 的发布吧。  
完了,我是这不是太多人的一部分了。 正准备好好发扬一下 PHPRPC 呢,这下受打击了。只有等到 3.0 来用了。

3.0 的草稿有看过一些, 好像是用 PHP5 写的,那PHP4 还能不能用呀?谢谢andot兄的回复!

作者: dennis   发布时间: 2006-08-23

不是用 PHP 5,还是 PHP 5 和 PHP 4 都支持。只是基本上统一了各个语言之间的接口,并且增加了密钥长度设置、输出捕获加密等特征。密钥交换更加智能,而不需要在由用户指定,而是在加密调用时自动完成。

作者: andot   发布时间: 2006-08-23

谢谢 andot !
支持你,期待 PHPRPC3.0 的释出.

作者: dennis   发布时间: 2006-08-24

热门下载

更多