1

好的,这是 Java/JavaScript 专家的一个:

在我的应用程序中,其中一个控制器将 TreeMap 传递给它的 JSP。该映射将汽车制造商的名称作为键,将汽车对象列表作为值。这些 Car 对象是包含汽车名称、ID、生产年份等的简单 bean。因此,地图看起来像这样(这只是一个示例,为了澄清一点):

键:保时捷
值:包含三个汽车对象的列表(例如 911、Carrera、Boxter 及其可敬的生产年限和 ID)
键:菲亚特
值:包含两个汽车对象的列表(例如,Punto 和 Uno)
等...

现在,在我的 JSP 中,我有两个组合框。一个应该收到汽车制造商的列表(地图上的键 - 这部分我知道该怎么做),另一个应该在用户从第一个组合框中选择某个制造商时动态更改以显示汽车的名称。因此,例如,用户在第一个组合框中选择“Porsche”,第二个组合框中立即显示“911,Carrera,Boxter”......

在花了几天试图找出如何做到这一点之后,我准备承认失败。我尝试了很多不同的东西,但每次我都会在路上的某个地方碰壁。有人可以建议我应该如何处理这个吗?是的,我是 JavaScript 新手,如果有人想知道...

编辑:我已将此重新标记为代码挑战。感谢任何在不使用任何 JavaScript 框架(如 JQuery)的情况下解决此问题的人。

4

7 回答 7

3

我只是喜欢挑战。

没有 jQuery,只是普通的 javascript,在 Safari 上测试过。

我想提前补充以下几点:

  • 由于错误检查,它很长。
  • 生成两个部分;带有 Map 的第一个脚本节点和制造商 SELECT 的内容
  • 适用于我的机器 (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>
于 2008-10-07T15:56:09.643 回答
2

无论如何,正如我所说,我终于设法自己做到了,所以这是我的答案......

我像这样从我的控制器收到地图(我正在使用 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]);
  }      
}

于 2008-10-09T13:38:21.700 回答
1

你在使用 Struts 吗?

您将需要一些 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 开发变得更加容易。文档非常好)。

我希望其中一些有意义?

于 2008-10-01T09:30:07.477 回答
1

这是一个在 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>
于 2008-10-08T17:34:11.333 回答
0

像这样的东西怎么样,使用原型?首先,您选择的类别框:

<SELECT onchange="changeCategory(this.options[this.selectedIndex].value); return false;">
   <OPTION value="#categoryID#">#category#</OPTION>
   ...

然后,您输出 N 个不同的选择框,每个子类别一个:

<SELECT name="myFormVar" class="categorySelect">
...                                        

您的 changeCategory javascript 函数禁用所有具有类 categorySelect 的选择,然后仅启用当前 categoryID 的选择。

// 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) 的选择,也只有活动的一个帖子。

于 2008-10-02T02:19:14.947 回答
0

不久前,我想到了类似的事情。

使用 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() );
  } );
);

其中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
    }
}

这应该可以解决问题。

请注意,其中可能存在语法错误;我已经编辑了我的代码以反映您的用例,并且不得不删除很多内容。

如果这有帮助,我将不胜感激。

于 2008-10-03T11:10:49.793 回答
0

作为我之前帖子的附加内容;您可以在您的 JSP 中放置一个脚本标签,以便在您的地图上进行迭代。可以在 Struts中的 Maps 中找到有关迭代地图的示例。

您想要实现的(如果您不关心表单提交)是我认为是这样的:

<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>

你仍然有一些多余的“”,你可能希望阻止它,但我认为这应该可以解决问题。

于 2008-10-04T13:27:24.190 回答