根据在另一个组合框中选择的内容,使用地图中的值动态填充组合框
Dynamicaly populating a combobox with values from a Map based on what's selected in another combobox
好的,这是Java/JavaScript大师的一个:
在我的应用程序中,其中一个控制器将树状图传递给它的 JSP。此映射将汽车制造商的名称作为键,将汽车对象列表作为值。这些 Car 对象是包含汽车名称、ID、生产年份等的简单豆子。所以,地图看起来像这样(这只是一个例子,为了澄清一下):
钥匙:保时捷
值:包含三个 Car 对象的列表(例如 911、Carrera、Boxter 及其可观的生产年份和 ID)
钥匙:菲亚特
值:包含两个 Car 对象(例如,Punto 和 Uno)
的列表等。。。
现在,在我的JSP中,我有两个组合框。一个人应该收到一个汽车制造商列表(地图上的键 - 这部分我知道怎么做),另一个应该动态更改以显示汽车名称当用户从第一个组合框中选择某个制造商时。因此,例如,用户在第一个组合框中选择一个"保时捷",第二个组合框立即显示"911,Carrera,Boxter"......
在花了几天时间试图找出如何做到这一点之后,我准备承认失败。我尝试了很多不同的东西,但每次我都会碰壁。谁能建议我应该如何处理这个问题?是的,我是一个JavaScript新手,如果有人想知道的话......
编辑:我已将其重新标记为代码挑战。向任何不使用任何JavaScript框架(如JQuery)解决这个问题的人致敬。
我就是喜欢挑战。
没有jQuery,只有普通的javascript,在Safari上测试过。
我想提前补充以下几点:
- 由于错误,它失败了很长时间检查。
- 生成两个部分;第一个带有映射的脚本节点和制造商的内容选择
- 适用于我的机器 (TM)(Safari/OS X)
- 没有 (css)应用样式。我的品味不好,所以反正也没用。
.
<body>
<script>
// DYNAMIC
// Generate in JSP
// You can put the script tag in the body
var modelsPerManufacturer = {
'porsche' : [ 'boxter', '911', 'carrera' ],
'fiat': [ 'punto', 'uno' ]
};
</script>
<script>
// STATIC
function setSelectOptionsForModels(modelArray) {
var selectBox = document.myForm.models;
for (i = selectBox.length - 1; i>= 0; i--) {
// Bottom-up for less flicker
selectBox.remove(i);
}
for (i = 0; i< modelArray.length; i++) {
var text = modelArray[i];
var opt = new Option(text,text, false, false);
selectBox.add(opt);
}
}
function setModels() {
var index = document.myForm.manufacturer.selectedIndex;
if (index == -1) {
return;
}
var manufacturerOption = document.myForm.manufacturer.options[index];
if (!manufacturerOption) {
// Strange, the form does not have an option with given index.
return;
}
manufacturer = manufacturerOption.value;
var modelsForManufacturer = modelsPerManufacturer[manufacturer];
if (!modelsForManufacturer) {
// This modelsForManufacturer is not in the modelsPerManufacturer map
return; // or alert
}
setSelectOptionsForModels(modelsForManufacturer);
}
function modelSelected() {
var index = document.myForm.models.selectedIndex;
if (index == -1) {
return;
}
alert("You selected " + document.myForm.models.options[index].value);
}
</script>
<form name="myForm">
<select onchange="setModels()" id="manufacturer" size="5">
<!-- Options generated by the JSP -->
<!-- value is index of the modelsPerManufacturer map -->
<option value="porsche">Porsche</option>
<option value="fiat">Fiat</option>
</select>
<select onChange="modelSelected()" id="models" size="5">
<!-- Filled dynamically by setModels -->
</select>
</form>
</body>
好吧,正如我所说,我终于设法自己做到了,所以这是我的答案......
我像这样从我的控制器接收地图(我正在使用 Spring,不知道它如何与其他框架配合使用):
<c:set var="manufacturersAndModels" scope="page" value="${MANUFACTURERS_AND_MODELS_MAP}"/>
这些是我的组合:
<select id="manufacturersList" name="manufacturersList" onchange="populateModelsCombo(this.options[this.selectedIndex].index);" >
<c:forEach var="manufacturersItem" items="<%= manufacturers%>">
<option value='<c:out value="${manufacturersItem}" />'><c:out value="${manufacturersItem}" /></option>
</c:forEach>
</select>
select id="modelsList" name="modelsList"
<c:forEach var="model" items="<%= models %>" >
<option value='<c:out value="${model}" />'><c:out value="${model}" /></option>
</c:forEach>
</select>
我导入了以下类(当然,某些名称已更改):
<%@ page import="org.mycompany.Car,java.util.Map,java.util.TreeMap,java.util.List,java.util.ArrayList,java.util.Set,java.util.Iterator;" %>
以下是完成所有艰苦工作的代码:
<script type="text/javascript">
<%
Map mansAndModels = new TreeMap();
mansAndModels = (TreeMap) pageContext.getAttribute("manufacturersAndModels");
Set manufacturers = mansAndModels.keySet(); //We'll use this one to populate the first combo
Object[] manufacturersArray = manufacturers.toArray();
List cars;
List models = new ArrayList(); //We'll populate the second combo the first time the page is displayed with this list
//initial second combo population
cars = (List) mansAndModels.get(manufacturersArray[0]);
for(Iterator iter = cars.iterator(); iter.hasNext();) {
Car car = (Car) iter.next();
models.add(car.getModel());
}
%>
function populateModelsCombo(key) {
var modelsArray = new Array();
//Here goes the tricky part, we populate a two-dimensional javascript array with values from the map
<%
for(int i = 0; i < manufacturersArray.length; i++) {
cars = (List) mansAndModels.get(manufacturersArray[i]);
Iterator carsIterator = cars.iterator();
%>
singleManufacturerModelsArray = new Array();
<%
for(int j = 0; carsIterator.hasNext(); j++) {
Car car = (Car) carsIterator.next();
%>
singleManufacturerModelsArray[<%= j%>] = "<%= car.getModel()%>";
<%
}
%>
modelsArray[<%= i%>] = singleManufacturerModelsArray;
<%
}
%>
var modelsList = document.getElementById("modelsList");
//Empty the second combo
while(modelsList.hasChildNodes()) {
modelsList.removeChild(modelsList.childNodes[0]);
}
//Populate the second combo with new values
for (i = 0; i < modelsArray[key].length; i++) {
modelsList.options[i] = new Option(modelsArray[key][i], modelsArray[key][i]);
}
}
你在使用支柱吗?
你需要一些JavaScript技巧(或AJAX)来完成这个。
你需要做的是,在你的JavaScript代码中的某个地方(暂时撇开你如何生成它):
var map = {
'porsche': [ 'boxter', '911', 'carrera' ],
'fiat': ['punto', 'uno']
};
这基本上是服务器端数据结构的副本,即由制造商键入的地图,每个值都有一个汽车类型数组。
然后,在制造商的onchange事件中,您需要从上面定义的映射中获取数组,然后从中创建选项列表。(查看 devguru.com - 它有很多关于标准JavaScript对象的有用信息)。
但是,根据您的汽车列表有多大,最好走AJAX路线。
您需要创建一个新的控制器,该控制器查找给定制造商的汽车类型列表,然后转发到返回JSON的JSP(它不一定是JSON,但它对我来说效果很好)。
然后,使用 jQuery 等库在 onchange 事件中检索制造商列表的汽车列表。(jQuery是一个优秀的JavaScript框架 - 它确实使JavaScript开发变得更加容易。文档非常好)。
我希望其中一些是有意义的?
这是一个在 jsp 中剪切和粘贴的工作答案,没有任何标签库或外部依赖项。带有模型的地图是硬编码的,但应该不会造成任何问题。
我将这个答案与我之前的答案分开,因为添加的 JSP 不会提高可读性。在"现实生活"中,我不会用所有的嵌入式逻辑来负担我的JSP,而是把它放在某个地方的类中。或者使用标签。
所有这些"第一个"东西都是为了防止生成的代码中出现多余的","。使用 foreach 不会让您了解元素的数量,因此您可以检查最后一个。您还可以跳过第一个元素处理,然后通过将生成器长度减少 1 来去除最后一个","。
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@page import="java.util.Map"%>
<%@page import="java.util.TreeMap"%>
<%@page import="java.util.Arrays"%>
<%@page import="java.util.Collection"%>
<%@page import="java.util.List"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Challenge</title>
</head>
<body onload="setModels()">
<% // You would get your map some other way.
Map<String,List<String>> map = new TreeMap<String,List<String>>();
map.put("porsche", Arrays.asList(new String[]{"911", "Carrera"}));
map.put("mercedes", Arrays.asList(new String[]{"foo", "bar"}));
%>
<%! // You may wish to put this in a class
public String modelsToJavascriptList(Collection<String> items) {
StringBuilder builder = new StringBuilder();
builder.append('[');
boolean first = true;
for (String item : items) {
if (!first) {
builder.append(',');
} else {
first = false;
}
builder.append('''').append(item).append('''');
}
builder.append(']');
return builder.toString();
}
public String mfMapToString(Map<String,List<String>> mfmap) {
StringBuilder builder = new StringBuilder();
builder.append('{');
boolean first = true;
for (String mf : mfmap.keySet()) {
if (!first) {
builder.append(',');
} else {
first = false;
}
builder.append('''').append(mf).append('''');
builder.append(" : ");
builder.append( modelsToJavascriptList(mfmap.get(mf)) );
}
builder.append("};");
return builder.toString();
}
%>
<script>
var modelsPerManufacturer =<%= mfMapToString(map) %>
function setSelectOptionsForModels(modelArray) {
var selectBox = document.myForm.models;
for (i = selectBox.length - 1; i>= 0; i--) {
// Bottom-up for less flicker
selectBox.remove(i);
}
for (i = 0; i< modelArray.length; i++) {
var text = modelArray[i];
var opt = new Option(text,text, false, false);
selectBox.add(opt);
}
}
function setModels() {
var index = document.myForm.manufacturer.selectedIndex;
if (index == -1) {
return;
}
var manufacturerOption = document.myForm.manufacturer.options[index];
if (!manufacturerOption) {
// Strange, the form does not have an option with given index.
return;
}
manufacturer = manufacturerOption.value;
var modelsForManufacturer = modelsPerManufacturer[manufacturer];
if (!modelsForManufacturer) {
// This modelsForManufacturer is not in the modelsPerManufacturer map
return; // or alert
}
setSelectOptionsForModels(modelsForManufacturer);
}
function modelSelected() {
var index = document.myForm.models.selectedIndex;
if (index == -1) {
return;
}
alert("You selected " + document.myForm.models.options[index].value);
}
</script>
<form name="myForm">
<select onchange="setModels()" id="manufacturer" size="5">
<% boolean first = true;
for (String mf : map.keySet()) { %>
<option value="<%= mf %>" <%= first ? "SELECTED" : "" %>><%= mf %></option>
<% first = false;
} %>
</select>
<select onChange="modelSelected()" id="models" size="5">
<!-- Filled dynamically by setModels -->
</select>
</form>
</body>
</html>
像这样的东西怎么样,使用原型?首先,您的类别选择框:
<SELECT onchange="changeCategory(this.options[this.selectedIndex].value); return false;">
<OPTION value="#categoryID#">#category#</OPTION>
...
然后,输出 N 个不同的选择框,每个子类别一个:
<SELECT name="myFormVar" class="categorySelect">
...
您的 changeCategory javascript 函数禁用所有具有类 categorySelect 的选择,然后只启用当前类别 ID 的选择。
// Hide all category select boxes except the new one
function changeCategory(categoryID) {
$$("select.categorySelect").each(function (select) {
select.hide();
select.disable();
});
$(categoryID).show();
$(categoryID).enable();
}
当您在原型中像这样隐藏/禁用时,它不仅会将其隐藏在页面上,而且会阻止该 FORM 变量发布。因此,即使您有 N 个具有相同 FORM 变量名称 (myFormVar) 的选择,也只有活动的 一个 发布。
不久前,我想到了类似的事情。
使用jQuery和TexoTela插件并不是那么困难。
首先,您有一个类似于上面提到的地图的数据结构:
var map = {
'porsche': [ 'boxter', '911', 'carrera' ],
'fiat': ['punto', 'uno']
};
您的 HTML 应该看起来与以下内容相当:
<select size="4" id="manufacturers">
</select>
<select size="4" id="models">
</select>
然后,你用jQuery代码填充第一个组合,如下所示:
$(document).ready(
function() {
$("#bronsysteem").change( manufacturerSelected() );
} );
);
其中制造商选择是在 onChange 事件上注册的回调
function manufacturerSelected() {
newSelection = $("#manufacturers").selectedValues();
if (newSelection.length != 1) {
alert("Expected a selection!");
return;
}
newSelection = newSelection[0];
fillModels(newSelection);
}
function fillModels(manufacterer) {
var models = map[manufacturer];
$("models").removeOption(/./); // Empty combo
for(modelId in models) {
model = models[modelId];
$("models").addOption(model,model); // Value, Text
}
}
这应该可以解决问题。
请注意,其中可能存在语法错误;我已经编辑了我的代码以反映您的用例,并且不得不剥离相当多。
如果这有帮助,我将不胜感激。
作为我上一篇文章的附加组件;您可以在 JSP 中放置一个脚本标记,用于迭代映射。有关迭代地图的示例可以在 Struts 中的地图中找到。
你想要实现的(如果你不关心表单提交)是我认为是这样的:
<script>
var map = {
<logic:iterate id="entry" name="myForm" property="myMap">
'<bean:write name=" user" property="key"/>' : [
<logic:iterate id="model" name="entry" property="value">
'<bean:write name=" model" property="name"/>' ,
</logic:iterate>
] ,
</logic:iterate>
};
</script>
你仍然有一些多余的",",你可能希望阻止,但我认为这应该可以解决问题。
- 谷歌地图固定位置覆盖
- 不显示带有本地json文件数据的谷歌地图脚本
- 谷歌地图不是以HTML显示,而是在JS Fiddle上工作
- 根据id将json数组组合为一个json数组
- 谷歌地图标记不会显示
- 无法在JS中显示谷歌地图
- 接受不在列表中的值-引导组合框
- 科尔多瓦页面类应用程序中的多个谷歌地图
- 需要帮助谷歌地图方向面板在FancyBox中显示
- 是否可以控制获取哪些Google地图脚本(JavaScript API)
- 在谷歌地图上获取事件的x,y坐标
- 谷歌地图API v3不适用于移动浏览器或PhoneGap
- 将组合JSON文件加载到谷歌地图
- 谷歌地图API JS-放置两个组合字段的自动完成
- 在谷歌地图的一个信息框中组合两个标记的内容
- 限制为 Google 地图 API V3 中融合表图层和 KML 图层的组合
- 如何在图像地图中组合热点
- KML信息窗口在谷歌地图中与Fusion Tables层组合时出现故障
- 谷歌地图图像叠加和多色标记 - 如何组合代码
- 根据在另一个组合框中选择的内容,使用地图中的值动态填充组合框