Динамическое заполнение комбинированного списка значениями из карты на основе того, что выбрано в другом комбинированном окне. - PullRequest
1 голос
/ 01 октября 2008

Хорошо, вот один для гуру Java / JavaScript:

В моем приложении один из контроллеров передает TreeMap в JSP. Эта карта содержит имена производителей автомобилей в качестве ключей и списки объектов автомобилей в качестве значений. Эти объекты Car являются простыми бобами, содержащими название автомобиля, идентификатор, год выпуска и т. Д. Итак, карта выглядит примерно так (это просто пример, чтобы немного прояснить ситуацию):

Ключ: Porsche
Значение: список, содержащий три объекта Car (например, 911, Carrera, Boxter с их респектабельными годами производства и идентификаторами)
Ключ: Fiat
Значение: список, содержащий два объекта Car (например, Punto и Uno)
и т.д ...

Теперь, в моем JSP у меня есть два комбинированных списка. Один должен получить список производителей автомобилей (ключи от карты - эту часть я знаю, как это сделать), а другой должен динамически изменить , чтобы отображать названия автомобилей, когда пользователь выбирает определенного производителя. из первого комбобокса. Так, например, пользователь выбирает «Porsche» в первом поле со списком, а второй сразу отображает «911, Carrera, Boxter» ...

Потратив пару дней, пытаясь выяснить, как это сделать, я готов признать поражение. Я перепробовал много разных вещей, но каждый раз, когда я врезался в стену, где-нибудь по пути. Кто-нибудь может подсказать, как мне подойти к этому? Да, я новичок в JavaScript, если кому-то было интересно ...

РЕДАКТИРОВАТЬ: я пометил это как вызов кода. Престижность любому, кто решает эту проблему без использования JavaScript-фреймворка (например, JQuery).

Ответы [ 7 ]

3 голосов
/ 07 октября 2008

Я просто люблю вызов.

Нет jQuery, просто обычный javascript, протестированный на Safari.

Я хотел бы заранее добавить следующие замечания:

  • Это очень долго из-за ошибки проверка.
  • Две части генерируются; первый скрипт-узел с картой и содержание производителя SELECT
  • Работает на моей машине (ТМ) (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>
2 голосов
/ 09 октября 2008

Ну, во всяком случае, как я уже сказал, мне наконец-то удалось это сделать самостоятельно, поэтому вот мой ответ ...

Я получаю карту от моего контроллера следующим образом (я использую 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]);
  }      
}

1 голос
/ 08 октября 2008

Вот рабочий, вырезанный и вставленный ответ в 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>
1 голос
/ 01 октября 2008

Вы используете Struts?

Для этого вам понадобится какая-то хитрость JavaScript (или AJAX).

То, что вам нужно сделать, это где-то в вашем коде JavaScript (оставляя в стороне, как вы генерируете его на минуту):

var map = {
   'porsche': [ 'boxter', '911', 'carrera' ],
   'fiat': ['punto', 'uno']
};

Это, в основном, копия вашей структуры данных на стороне сервера, то есть карта с указанием производителя, каждое значение имеет массив типов автомобилей.

Затем в вашем событии onchange для производителей вам нужно будет получить массив из карты, определенной выше, а затем создать список опций из этого. (Проверьте devguru.com - он содержит много полезной информации о стандартных объектах JavaScript).

Однако, в зависимости от того, насколько велик ваш список автомобилей, лучше всего пойти по маршруту AJAX.

Вам необходимо создать новый контроллер, который просматривает список типов автомобилей, указанных производителем, а затем переходит к JSP, который возвращает JSON (это не обязательно должен быть JSON, но у меня это работает довольно хорошо).

Затем используйте библиотеку, такую ​​как jQuery , чтобы получить список автомобилей в вашем событии onchange для списка производителей. (jQuery - это отличная JavaScript-инфраструктура, которая делает разработку с использованием JavaScript намного проще. Документация очень хорошая).

Надеюсь, что-то из этого имеет смысл?

0 голосов
/ 04 октября 2008

как дополнение к моему предыдущему посту; Вы можете поместить тег сценария в свой 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>

У вас все еще есть несколько лишних ",", которые вы, возможно, захотите предотвратить, но я думаю, что это должно сработать.

0 голосов
/ 03 октября 2008

Не так давно я думал о чем-то похожем.

Используя 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
    }
}

Это должно сработать.

Обратите внимание, что там могут быть синтаксические ошибки; Я отредактировал свой код, чтобы отразить ваш вариант использования, и пришлось удалить довольно много.

Если это поможет, я буду признателен за комментарий.

0 голосов
/ 02 октября 2008

Как насчет этого, используя прототип? Во-первых, ваш выбор категории:

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

Затем вы выводите N различных полей выбора, по одному для каждой из подкатегорий:

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

Ваша функция JavaScript changeCategory отключает все выборки с помощью класса 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), только один активный отправляет сообщения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...