MySQL / PHP / JS动态填充下拉菜单

时间:2011-08-02 23:03:30

标签: php javascript mysql html mysqli

我正在开发基于数据库/网络的前端,并遇到了一个问题。首先,我有一个带有下拉菜单的表单,其中包含合同列表。选择合同后,我希望与该合同相关的工作(从MySQL数据库获取)填充第一个下方的第二个下拉菜单。

我会在一个菜单中获得所有信息,但是8000条目下拉菜单有点笨拙。

我的PHP和HTML几乎无法通过,但足以达到我的目的,但是我的ECMA体验仅限于Flash MX中的一些ActionScript,很多年前。 如果可能的话,我想避免使用第三方JS库(例如jQuery),我不介意编写更多代码。我只需要知道这是否可行,并且朝着正确的方向发展。

我现在会闭嘴,下面是获取合同ID(以及相关客户)的表格,以及未完成的工作菜单。

<select name='idcontract' onchange=''> 
<!--fetch/display contracts/clients-->
    <?php
        include 'sqldb.php';
        $cntqres = mysqli_query($dbc, 'SELECT * FROM contract');
        while ($cntrow = mysqli_fetch_array($cntqres))
        {
            $cliqres = mysqli_query($dbc, "SELECT * FROM client WHERE idclient = '$cntrow[idclient]'");
            while ($clirow = mysqli_fetch_array($cliqres))
            {
                echo "<option value='$cntrow[idcontract]'>$cntrow[idcontract] $clirow[name]</option>";
            }
        }
    ?>
</select>
<select name='idjob'>
    <option value='NULL'>Please select a contract</option>
<!--here goes the magical piece of code I don't know how to write-->
</select>

非常感谢任何帮助。

编辑:

这是由FeatherAJAX调用的PHP:

<?php
    include 'sqldb.php';
    $cnt = mysqli_real_escape_string($dbc, $_GET['cnt']);
    $sql = "SELECT * FROM job WHERE idcontract='$cnt' ORDER BY job.idjob";
    $jqres = mysqli_query($dbc, $sql);
    $i = 1;
    while (($jrow = mysqli_fetch_array($jqres)) && ($i < count($jrow)))
    {
        echo "idjob=><option value='$jrow[idjob]' id='$jrow[idjob]'>Job-$i $jrow[part_desc]</option>";
        $i++;
    }
?>

1 个答案:

答案 0 :(得分:2)

首先,您可能想要重写产生合同选项的代码块。循环查询结果并对每个记录执行另一个查询是低效的。根据您的查询,您可以使用此代码,该代码执行单个查询,然后根据该代码生成选项。 (我必须在ORDER子句中使用虚构的列名。通常,您应该始终对记录集进行排序,以便结果处于确定的顺序 - 即使您不关心该顺序是什么。

<select name="idcontract" id="idcontract"> 
<!--fetch/display contracts/clients-->
<?php
include 'sqldb.php';
$clients = mysqli_query($dbc, '         
    SELECT  ct.idcontract, ct.idclient, cl.name
    FROM    contract ct LEFT OUTER JOIN client cl ON ct.idclient = cl.idclient
    ORDER BY ct.contractname, cl.clientname
    ');

while ($client = mysqli_fetch_array($clients)) {
    echo "<option value=\"{$client[idcontract]}\">{$client[idcontract} {$client[name]}</option>";
}
?>
</select>
<select name="idjob" id="idjob">
    <option value="NULL">Please select a contract</option>
</select>

对于您的问题,您正在寻找的代码实际上不会发表评论的位置。你需要的是一个事件处理程序,它响应用户在第一个SELECT中选择一个选项;然后它应该获取该选项的值,并从服务器请求一组键值对填充到第二个SELECT中。

这样的事情:

document.getElementById('idcontract').onchange = function(event) {
    // grab currently selected value
    var sValue = null;
    for(var i = 0, imax = this.childNodes.length; i < imax; i++) {
        var eOption = this.childNodes[i]; // shorthand
        if(eOption.selected) {
            sValue = eOption.value;
            break;
        }
    }

    if(!sValue) return;

    // get the sub-options for this value
    getSubOptions(sValue, function(XHR) {
        // this code runs once the response comes back from the server
        var aPairs = [];
        var nlJobs = XHR.getElementsByTagName('jobs'); // assumptions #1 & #2: response is XML, includes <job> tag for each job

        // extract key-value pairs from XML
        for(var i = 0, imax = nlJobs.length; i < imax; i++) {
            var xJob = nlJobs[i]; // shorthand
            /*
                assumption #3: <job> tag has "id" property
                assumption #4: job name appears inside <job> tag
                assumption #4.5: you've got an abstraction layer that normalizes XML node interfaces so that "text" and "textContent" are folded into "textContent"
            */
            aPairs.push({ 'key': xJob.getAttribute('id'), 'value': xJob.textContent });
        }

        // given array of key-value pairs, rebuild select box
        var eJobs = document.getElementById('idjob');

        setOptions(eJobs, aPairs);
    });
}

function setOptions(eNode, aPairs) {
    if(!eNode || !eNode.nodeName || eNode.nodeName.toUpperCase() !== 'SELECT') return false;

    // empty SELECT of all options
    while(eNode.firstChild) {
        eNode.removeChild(eNode.firstChild);
    }

    // build up new nodes
    var eOpt = null;
    for(var i = 0, imax = aPairs.length; i < imax; i++) {
        eOpt = document.createElement('OPTION');
        eOpt.value = aPairs[i].key;
        eOpt.appendChild(document.createTextNode(aPairs[i].value));
        eNode.appendChild(eOpt);
    }
    return true;
}

当然,这是一个重要的部分:你需要某种AJAX抽象层。你不需要从框架中获得它,并且一个好的库可以少于50行代码(例如,参见quirksmode.org上的PPK的ajax脚本),但你绝对需要一些东西。该层将提供两个好处:(1)跨浏览器兼容性; (2)句法糖。

例如,上面的代码不包含getSubOptions的定义。那是因为逻辑会根据您的AJAX抽象提供的接口而有所不同。但是,您的想法是,您将针对您编写的接受参数并返回满足该请求的数据的脚本执行GET请求。在上面的代码中,我假装您编写的脚本将返回正确格式的XML数据,其MIME类型标识它。或者,您可以使用JSON(或JSONP),直接文本(例如CSV样式数据),甚至是刚刚插入页面的原始HTML。

使用完整框架的好处是它们都提供了方便的DOM操作方法(即再次使用语法糖)。

底线:你绝对可以用自己开发的方法做到这一点(我很自豪地说我自己做过)。但这需要更长的时间 - 不仅仅是因为它不太方便,还因为你必须重新发明轮子===在你的代码中找到并修复bug,而不是利用一些库中经过充分测试的核心组件。 / p>

编辑:如果你想使用JSON作为数据交换格式而不是XML,你可以修改传递给getSubOptions的响应处理程序,如下所示:

getSubOptions(sValue, function(XHR) {
     // this code runs once the response comes back from the server
    var aPairs = eval(XHR.responseText); // assumes JSON defines an array of key-value pairs

    // given array of key-value pairs, rebuild select box
    var eJobs = document.getElementById('idjob');

    setOptions(eJobs, aPairs);
});

以下是JSON的样子:

[ { key: '1234', value: 'Job #1' },
  { key: '2345', value: 'Job #2' },
  ...
];

在此示例中,JSON结构方便地镜像setOptions所期望的属性名称;这就是说,keyvalue似乎非常无害。

如果您已开始使用JSON进行数据,则可能需要将JSONP视为更安全的替代方案。它真的很相似,但设计模式与上面的匿名回调技术略有不同。

编辑2:为响应者修改了示例代码:

<?php
include 'sqldb.php';
$cnt = mysqli_real_escape_string($dbc, $_GET['cnt']);
$sql = "SELECT * FROM job WHERE idcontract='$cnt' ORDER BY job.idjob";
$jqres = mysqli_query($dbc, $sql);
$i = 1;

// prepare the response
header('Content-Type: text/html');

while (($jrow = mysqli_fetch_array($jqres)) && ($i < count($jrow))) {
    echo "<option value=\"$jrow[idjob]\" id=\"$jrow[idjob]\">Job-$i ${htmlentities(jrow[part_desc])}</option>";
    $i++;
}
?>